/*Partially Implements the client side of the HTTP 1.1 Protocol as defined in RFC 2616,2617*/

//#include "httpclient.h"
#include "httpclient_auth.h"     /* Crypto support (Digest, MD5) */
#include "httpclient_string.h"   /* String utilities */
#include "httpclient.h"

/* HTTP User credentials */
typedef struct _httpclientCredentials_t {
    CHAR                   CredUser[HTTPCLIENT_MAX_USERNAME_LENGTH];
    CHAR                   CredPassword[HTTPCLIENT_MAX_PASSWORD_LENGTH];
    CHAR                   AuthSchemaName[16];  /* The authentication schema name (for string comperission) */
    httpclientAuthSchema_e CredAuthSchema;      /* The schema that calle has selected for the session */
    BOOL                   Authentication;      /* a flag that indicates that this session has requested a user authentication */
} httpclientCredentials_t;

/* HTTP Counters */
typedef struct _httpclientCounters_t {
    UINT32   nRecivedHeaderLength;   /* Bytes count of the incoming header */
    UINT32   nRecivedBodyLength;     /* Bytes count of the incoming body length */
    UINT32   nRecivedChunkLength;    /* The next chunk length in bytes */
    UINT32   nBytesToNextChunk;      /* How many bytes we have to read until we can expect the next chunk */
    UINT32   nActionStartTime;       /* Operation start time */
    UINT32   nActionTimeout;         /* Timeout for the session */
    UINT32   nSentChunks;            /* Count of sent chunks */
    UINT32   nSentBodyBytes;         /* Count of body bytes that ware sent */
    UINT32   nSentHeaderBytes;       /* Count of header bytes thhat ware sent */
} httpclientCounters_t;

/* HTTP Client Session data */
typedef struct _httpclientSession_t {
    httpclientUrl_t         HttpUrl;
    httpclientHeaders_t     HttpHeaders;
    httpclientHeadersInfo_t HttpHeadersInfo;
    httpclientAuthHeader_t  HttpAuthHeader;
    httpclientProxy_t       HttpProxy;
    httpclientCredentials_t HttpCredentials;
    httpclientConnection_t  HttpConnection;
    httpclientCounters_t    HttpCounters;
    UINT32                  HttpState;
    UINT32                  HttpFlags;
#if defined(_HTTP_DEBUGGING_)
    E_HTTPDebug             *pDebug;
#endif
} httpclientSession_t, *P_HTTP_SESSION;

/* HTTP API private function */
//static UINT32 HTTPClientSetLocalConnection  (HTTPCLIENT_SESSION_HANDLE pSession, UINT32 nPort);
static UINT32 HTTPIntrnResizeBuffer         (P_HTTP_SESSION pHTTPSession, UINT32 nNewSize);
static UINT32 HTTPIntrnSetURL               (P_HTTP_SESSION pHTTPSession, CHAR *pUrl,UINT32 nUrlLength);
static UINT32 HTTPIntrnConnectionClose      (P_HTTP_SESSION pHTTPSession);
static UINT32 HTTPIntrnConnectionOpen       (P_HTTP_SESSION pHTTPSession);
static UINT32 HTTPIntrnGetRemoteHeaders     (P_HTTP_SESSION pHTTPSession);
static UINT32 HTTPIntrnGetRemoteChunkLength (P_HTTP_SESSION pHTTPSession);
static UINT32 HTTPIntrnSend                 (P_HTTP_SESSION pHTTPSession, CHAR *pData,UINT32 *nLength);
static UINT32 HTTPIntrnRecv                 (P_HTTP_SESSION pHTTPSession, CHAR *pData,UINT32 *nLength,BOOL PeekOnly);
static UINT32 HTTPIntrnParseAuthHeader      (P_HTTP_SESSION pHTTPSession);
static UINT32 HTTPIntrnAuthHandler          (P_HTTP_SESSION pHTTPSession);
static UINT32 HTTPIntrnAuthSendDigest       (P_HTTP_SESSION pHTTPSession);
static UINT32 HTTPIntrnAuthSendBasic        (P_HTTP_SESSION pHTTPSession);
static UINT32 HTTPIntrnAuthenticate         (P_HTTP_SESSION pHTTPSession);
static UINT32 HTTPIntrnHeadersAdd           (P_HTTP_SESSION pHTTPSession, CHAR *pHeaderName, UINT32 nNameLength, CHAR *pHeaderData, UINT32 nDataLength);
static UINT32 HTTPIntrnHeadersRemove        (P_HTTP_SESSION pHTTPSession, CHAR *pHeaderName);
static UINT32 HTTPIntrnHeadersReceive       (P_HTTP_SESSION pHTTPSession, UINT32 nTimeout);
static UINT32 HTTPIntrnHeadersSend          (P_HTTP_SESSION pHTTPSession, httpclientVerb_e HttpVerb);
static UINT32 HTTPIntrnHeadersParse         (P_HTTP_SESSION pHTTPSession);
static UINT32 HTTPIntrnHeadersFind          (P_HTTP_SESSION pHTTPSession, CHAR *pHeaderName, httpclientParam_t *pParam,BOOL IncommingHeaders,UINT32 nOffset);
static UINT32 HTTPIntrnSessionReset         (P_HTTP_SESSION pHTTPSession, BOOL EntireSession);
static UINT32 HTTPIntrnSessionGetUpTime     (VOID);
static BOOL   HTTPIntrnSessionEvalTimeout   (P_HTTP_SESSION pHTTPSession);

#ifndef _WIN32
/**
 * _RCW_ : Added functions.
 */
#include	<fcntl.h>
/**
 * Here to set a socket to nonblocking (or not).
 */
static int sktSetNonblocking( int socket , int on_off )
{
    int			flags_orig , flags_new;

    // Get current socket flags; return if error.
    flags_orig = fcntl( socket , F_GETFL , 0 );
    if ( flags_orig < 0 )
        return -1;

    // Adjust flags.
    if ( on_off != 0 )
        flags_new = flags_orig | O_NONBLOCK;
    else
        flags_new = flags_orig & ~O_NONBLOCK;

    // Set socket to blocking/nonblocking; return if error.
    if ( fcntl( socket , F_SETFL , flags_new ) < 0 )
    {
        // Set flags back to where they were.
        fcntl( socket , F_SETFL , flags_orig );
        // And return.
        return -1;
    }
    return 0;
}

#endif

/*
static UINT32 HTTPClientSetLocalConnection  (HTTPCLIENT_SESSION_HANDLE pSession, UINT32 nPort)
{

    return 0;
}
*/

HTTPCLIENT_SESSION_HANDLE  httpclient_open(HTTPCLIENT_SESSION_FLAGS flags)
{
    P_HTTP_SESSION pHTTPSession = NULL;         // Handle to the session pointer
    UINT32         nAllocationSize;             // Size of the dynamically allocated buffer

    #if defined(__HARMONY_OS__)
    pHTTPSession = (P_HTTP_SESSION)malloc(sizeof(httpclientSession_t));
    #else
    pHTTPSession = (P_HTTP_SESSION)malloc(ALIGN(sizeof(httpclientSession_t)));
    #endif
    if(!pHTTPSession) {
        return 0;
    }
    //printf("Session Ptr lives at %p.\n", (void*)&pHTTPSession);
    memset(pHTTPSession,0x00,sizeof(httpclientSession_t));

    /* Allocate space for the incoming and outgoing headers
       Check if the headers buffer is resizable */
    if(HTTPCLIENT_MEMORY_RESIZABLE) {
        /* Memory is resizable, so use the init defined size or the maximum buffer size (which ever is smaller) */
        nAllocationSize = MIN(HTTPCLIENT_MAX_SEND_RECV_HEADERS,HTTPCLIENT_INIT_SEND_RECV_HEADERS);
    }
    else {
        // Memory is not resizable so simply use the maximum defined size
        nAllocationSize = HTTPCLIENT_MAX_SEND_RECV_HEADERS;
    }

    /* Allocate the headers buffer */
    #if defined(__HARMONY_OS__)
    pHTTPSession->HttpHeaders.HeadersBuffer.pParam = (CHAR*)malloc(nAllocationSize);
    #else
    pHTTPSession->HttpHeaders.HeadersBuffer.pParam = (CHAR*)malloc(ALIGN(nAllocationSize));
    #endif
    /* Check the returned pointer */
    if(!pHTTPSession->HttpHeaders.HeadersBuffer.pParam) {
        /* malloc() error, free the containing structure and exit. */
        free(pHTTPSession);
        return 0;
    }

    /* Reset the headers allocated memory */
    memset(pHTTPSession->HttpHeaders.HeadersBuffer.pParam ,0x00,nAllocationSize);
    /* Set the buffer length */
    pHTTPSession->HttpHeaders.HeadersBuffer.nLength = nAllocationSize;
    /* Set default values in the session structure */
    httpclient_set_verb((UINTP)pHTTPSession,(httpclientVerb_e)HTTPCLIENT_DEFAULT_VERB);
    pHTTPSession->HttpUrl.nPort             = HTTPCLIENT_DEFAULT_PORT;
    pHTTPSession->HttpConnection.HttpSocket = HTTP_INVALID_SOCKET;
    /* Set the outgoing headers pointers */
    pHTTPSession->HttpHeaders.HeadersOut.pParam = pHTTPSession->HttpHeaders.HeadersBuffer.pParam;
    /* Set our state */
    pHTTPSession->HttpState = pHTTPSession->HttpState | HTTPCLIENT_STATE_INIT;
    /* Save the flags */
    pHTTPSession->HttpFlags = flags;
    /* Reset the status */
    pHTTPSession->HttpHeadersInfo.nHTTPStatus = 0;

    /* Return an allocated session pointer (cast to HTTPCLIENT_SESSION_HANDLE first) */
    return (HTTPCLIENT_SESSION_HANDLE)pHTTPSession;
}

HTTPCLIENT_RESULT  httpclient_reset(HTTPCLIENT_SESSION_HANDLE pSession)
{
    P_HTTP_SESSION pHTTPSession = NULL;


    /* Cast the handle to our internal structure and check the pointers validity first */
    pHTTPSession = (P_HTTP_SESSION)pSession;
    if(!pHTTPSession) {
        return HTTPCLIENT_ERROR_INVALID_HANDLE;
    }

    return HTTPIntrnSessionReset(pHTTPSession,TRUE);
}

HTTPCLIENT_RESULT httpclient_close(HTTPCLIENT_SESSION_HANDLE *pSession)
{
    P_HTTP_SESSION pHTTPSession = NULL;


    /* Cast the handle to our internal structure and check the pointers validity first */
    pHTTPSession = (P_HTTP_SESSION)*(pSession);
    if(!pHTTPSession) {
        return HTTPCLIENT_ERROR_INVALID_HANDLE;
    }

    /* Check for a valid pointer to the HTTP headers */
    if(pHTTPSession->HttpHeaders.HeadersBuffer.pParam) {
        /* Release the used memory */
        free(pHTTPSession->HttpHeaders.HeadersBuffer.pParam);
    }
    /* Close any active socket connection */
    HTTPIntrnConnectionClose(pHTTPSession);
    /* free the session structure */
    free(pHTTPSession);

    pHTTPSession = 0;   /* NULL the pointer */
    *(pSession) = 0;

    return HTTPCLIENT_SUCCESS;
}

HTTPCLIENT_RESULT httpclient_set_verb(HTTPCLIENT_SESSION_HANDLE pSession,
                           httpclientVerb_e HttpVerb)
{
    P_HTTP_SESSION pHTTPSession = NULL;


    /* Cast the handle to our internal structure and check the pointers validity first */
    pHTTPSession = (P_HTTP_SESSION)pSession;
    if(!pHTTPSession) {
        return HTTPCLIENT_ERROR_INVALID_HANDLE;
    }

#if defined(_HTTP_DEBUGGING_)
    if(pHTTPSession->pDebug) {
        pHTTPSession->pDebug("httpclient_set_verb",NULL,0,"Selected Verb is %d",(INT32)HttpVerb);
    }
#endif

    /* Cache the verb (as integer) for later use */
    pHTTPSession->HttpHeaders.HttpVerb = HttpVerb;

    /* Convert the Verb parameter into its equivalent string representation */
    switch (HttpVerb) {
    case HTTPCLIENT_GET:
        strcpy(pHTTPSession->HttpHeaders.Verb, "GET");
        break;
    case HTTPCLIENT_HEAD:
        if(!HTTPCLIENT_ALLOW_HEAD_VERB)
        {
            return HTTPCLIENT_ERROR_BAD_VERB;
        }
        strcpy(pHTTPSession->HttpHeaders.Verb, "HEAD");
        break;
    case HTTPCLIENT_POST:
        strcpy(pHTTPSession->HttpHeaders.Verb, "POST");
        break;
    case HTTPCLIENT_PUT:
        strcpy(pHTTPSession->HttpHeaders.Verb, "PUT");
        break;
    default:
        /* Unknown verb */
        return HTTPCLIENT_ERROR_BAD_VERB;
    };

    return HTTPCLIENT_SUCCESS;
}

HTTPCLIENT_RESULT httpclient_set_auth(HTTPCLIENT_SESSION_HANDLE pSession,
                           httpclientAuthSchema_e AuthSchema,
                           void *pReserved)
{
    P_HTTP_SESSION pHTTPSession = NULL;


    /* Cast the handle to our internal structure and check the pointers validity first */
    pHTTPSession = (P_HTTP_SESSION)pSession;
    if(!pHTTPSession) {
        return HTTPCLIENT_ERROR_INVALID_HANDLE;
    }
#if defined(_HTTP_DEBUGGING_)
    if(pHTTPSession->pDebug) {
        pHTTPSession->pDebug("httpclient_set_auth",NULL,0,"Selected authentication is %d",(INT32)AuthSchema);
    }
#endif

    switch(AuthSchema) {
	    case(HTTPCLIENT_AUTHSCHEMA_BASIC):
	        strcpy(pHTTPSession->HttpCredentials.AuthSchemaName,"basic");
	        break;
	    case(HTTPCLIENT_AUTHSCHEMA_DIGEST):
	        strcpy(pHTTPSession->HttpCredentials.AuthSchemaName,"digest");
	        break;
	    case(HTTPCLIENT_AUTHSCHEMA_KERBEROS):
	        strcpy(pHTTPSession->HttpCredentials.AuthSchemaName,"negotiate");
	        break;
		case(HTTPCLIENT_AUTHSCHEMA_BEARER): /* [dx.jiang]20180823 add for oauth2.0 token. */
	        strcpy(pHTTPSession->HttpCredentials.AuthSchemaName,"bearer");
			/* Get the length of the bearer string and see if it's not too long */
			if(strlen((char*)pReserved) > HTTPCLIENT_MAX_PASSWORD_LENGTH) {
				return HTTPCLIENT_ERROR_LONG_INPUT;
			}
			/* Copy them into our internal buffer */
			strcpy(pHTTPSession->HttpCredentials.CredPassword, (char*)pReserved);
	        break;
	    default:
	    	break;
    };

    if(AuthSchema >= HTTPCLIENT_AUTHSCHEMA_NOSUPPORT) {
        return HTTPCLIENT_ERROR_BAD_AUTH;
    }

    pHTTPSession->HttpCredentials.CredAuthSchema = AuthSchema;
    return HTTPCLIENT_SUCCESS;
}

HTTPCLIENT_RESULT httpclient_set_proxy(HTTPCLIENT_SESSION_HANDLE pSession,
		CHAR *pProxyHost, UINT16 nPort, CHAR *pUserName, CHAR *pPassword)
{
    P_HTTP_SESSION pHTTPSession = NULL;


    /* Cast the handle to our internal structure and check the pointers validity first */
    pHTTPSession = (P_HTTP_SESSION)pSession;
    if(!pHTTPSession) {
        return HTTPCLIENT_ERROR_INVALID_HANDLE;
    }
#if defined(_HTTP_DEBUGGING_)
    if(pHTTPSession->pDebug) {
        pHTTPSession->pDebug("httpclient_set_proxy",NULL,0,"Proxy host %s,Port %d, Username %s,Password %s",pProxyHost,nPort,pUserName,pPassword);
    }
#endif

    /* Cache the user supplied information in the internal session structure */
    strncpy(pHTTPSession->HttpProxy.ProxyHost,pProxyHost,HTTPCLIENT_MAX_PROXY_HOST_LENGTH);
    if(pUserName) {
        /* Proxy user name (for Proxy server authentication) */
        strncpy(pHTTPSession->HttpProxy.ProxtUser,pUserName,HTTPCLIENT_MAX_USERNAME_LENGTH);
    }
    if(pPassword) {
        /* Proxy password (for proxy server authentication) */
        strncpy(pHTTPSession->HttpProxy.ProxyPassword,pPassword,HTTPCLIENT_MAX_PASSWORD_LENGTH);
    }
    /* Proxy TCP port */
    pHTTPSession->HttpProxy.nProxyPort = nPort;
    /* Set the Proxy flag in the connection structure */
    pHTTPSession->HttpFlags = pHTTPSession->HttpFlags | HTTPCLIENT_FLAG_USINGPROXY;
    /* Set the proxy auyjentication schema */
    if(pPassword && pUserName) {
        pHTTPSession->HttpProxy.ProxyAuthSchema = (httpclientAuthSchema_e)HTTPCLIENT_DEFAULT_PROXY_AUTH;
    }
    return HTTPCLIENT_SUCCESS;
}

HTTPCLIENT_RESULT httpclient_set_credentials(HTTPCLIENT_SESSION_HANDLE pSession,
		CHAR *pUserName, CHAR *pPassword)
{
    UINT32  nLength;
    P_HTTP_SESSION pHTTPSession = NULL;


    /* Cast the handle to our internal structure and check the pointers validity first */
    pHTTPSession = (P_HTTP_SESSION)pSession;
    if(!pHTTPSession) {
        return HTTPCLIENT_ERROR_INVALID_HANDLE;
    }

    /* Get the length of the user name string and see if it's not too long */
    nLength = strlen(pUserName);
    if(nLength > HTTPCLIENT_MAX_USERNAME_LENGTH) {
        return HTTPCLIENT_ERROR_LONG_INPUT;
    }

    /* Get the length of the password string and see if it's not too long */
    nLength = strlen(pPassword);
    if(nLength > HTTPCLIENT_MAX_PASSWORD_LENGTH) {
        return HTTPCLIENT_ERROR_LONG_INPUT;
    }

    /* Copy them into our internal buffer */
    strcpy(pHTTPSession->HttpCredentials.CredUser ,pUserName);
    strcpy(pHTTPSession->HttpCredentials.CredPassword ,pPassword);

    /* Set the authentication Boolean flag */
    pHTTPSession->HttpHeadersInfo.HaveCredentials = TRUE;

    return HTTPCLIENT_SUCCESS;
}

/**
 * \brief Http Session Headers Set.
 *
 * \details Add headers to the outgoing request.
 *
 * \param pSession    [in]HTTP Session Handle.
 * \param pHeaderName [in]The Headers name.
 * \param pHeaderData [in]The headers data.
 * \param nInsert     [in]Reserved could be any.
 *
 * \return Please refer to #HTTPCLIENT_RESULT.
 */
HTTPCLIENT_RESULT httpclient_add_headers(HTTPCLIENT_SESSION_HANDLE pSession,
		CHAR *pHeaderName, CHAR *pHeaderData, BOOL nInsert)
{

    UINT32  nRetCode;
    UINT32  nHeaderLength,nDataLength;
    P_HTTP_SESSION pHTTPSession = NULL;

	UNUSED(nInsert);

    /* Cast the handle to our internal structure and check the pointers validity */
    pHTTPSession = (P_HTTP_SESSION)pSession;
    if(!pHTTPSession)
    {
        return HTTPCLIENT_ERROR_INVALID_HANDLE;
    }

#if defined(_HTTP_DEBUGGING_)
    if(pHTTPSession->pDebug)
    {
        pHTTPSession->pDebug("httpclient_add_headers",NULL,0,"Adding Header %s: %s",pHeaderName,pHeaderData);
    }
#endif

    /* Get the elements length */
    nHeaderLength = strlen(pHeaderName);
    nDataLength   = strlen(pHeaderData);

    /* Call the internal function to add the headers to our session buffer */
    nRetCode = HTTPIntrnHeadersAdd(pHTTPSession,pHeaderName,nHeaderLength,pHeaderData,nDataLength);

    return nRetCode;
}

HTTPCLIENT_RESULT httpclient_send_request(HTTPCLIENT_SESSION_HANDLE pSession,
	CHAR *pUrl, VOID *pData, UINT32 nDataLength, UINT32 TotalLength, UINT32 nTimeout, UINT32 nClientPort)
{
    UINT32          nRetCode;
    UINT32          nBytes;       /* Bytes counter (socket operations) */
    UINT32          nUrlLength;   /* Length of the given Url */
    P_HTTP_SESSION  pHTTPSession = NULL;
    CHAR            ContentLength[32];

    do {
        /* Cast the handle to our internal structure and check the pointers validity */
        pHTTPSession = (P_HTTP_SESSION)pSession;
        if(!pHTTPSession) {
            nRetCode = HTTPCLIENT_ERROR_INVALID_HANDLE;
            break;
        }
#if defined(_HTTP_DEBUGGING_)
        if(pHTTPSession->pDebug) {
            pHTTPSession->pDebug("httpclient_send_request",NULL,0,"Url: %s",pUrl);
        }
#endif

        /* Set the operation timeout counters */
        pHTTPSession->HttpCounters.nActionStartTime = HTTPIntrnSessionGetUpTime();
        /* 0 makes us use the default defined value */
        pHTTPSession->HttpCounters.nActionTimeout = HTTP_TIMEOUT(nTimeout);
        /* Store the cliebt port for later usage */
        pHTTPSession->HttpConnection.HttpClientPort = nClientPort;

        /* Parse the URL */
        nUrlLength = strlen(pUrl);
        nRetCode = HTTPIntrnSetURL(pHTTPSession,pUrl,nUrlLength);
        if(nRetCode != HTTPCLIENT_SUCCESS) {
            break;
        }

        /* Create the default headers */
        /* Add the "Host" header. we should handle a special case of port incorporated within the host name. */
        if((pHTTPSession->HttpFlags & HTTPCLIENT_FLAG_URLANDPORT) != HTTPCLIENT_FLAG_URLANDPORT) {
            /* The case where we don't have the port */
            if((nRetCode = HTTPIntrnHeadersAdd(pHTTPSession,
                "Host",4,
                pHTTPSession->HttpUrl.UrlHost.pParam,
                pHTTPSession->HttpUrl.UrlHost.nLength)) != HTTPCLIENT_SUCCESS) {
                break;
            }
        }
        else {
            /* We have the port so use a deferent element */
            if((nRetCode = HTTPIntrnHeadersAdd(pHTTPSession,"Host",4,pHTTPSession->HttpUrl.UrlHost.pParam,
                (pHTTPSession->HttpUrl.UrlPort.pParam - pHTTPSession->HttpUrl.UrlHost.pParam) -1 )) != HTTPCLIENT_SUCCESS) {
                break;
            }
        }
        /* We are in a post request without knowing the total length in advance so return error or use chunking */
        if((pHTTPSession->HttpHeaders.HttpVerb == HTTPCLIENT_POST || pHTTPSession->HttpHeaders.HttpVerb == HTTPCLIENT_PUT) && TotalLength == 0) {
            /* If the user specified the chunked flag */
            if((pHTTPSession->HttpFlags & HTTPCLIENT_FLAG_SEND_CHUNKED) == HTTPCLIENT_FLAG_SEND_CHUNKED) {
                /* Add the  Transfer-Encoding:  header */
                if((nRetCode = HTTPIntrnHeadersAdd(pHTTPSession,"Transfer-Encoding",17,"chunked",7))!= HTTPCLIENT_SUCCESS) {
                    break;;
                }
            }
            else {
                /* Not a supported operation - unknown length */
                nRetCode = HTTPCLIENT_ERROR_HEADER_NO_LENGTH;
                break;
            }
        }

        /* Add the "User-Agent" header */
        //if((nRetCode = HTTPIntrnHeadersAdd(pHTTPSession,"User-Agent",10,HTTPCLIENT_DEFAULT_AGENT,strlen(HTTPCLIENT_DEFAULT_AGENT)))!= HTTPCLIENT_SUCCESS) {
        //    break;
        //}
        /* Add the "Keep-Alive" header (if requested by the caller) */
        if((pHTTPSession->HttpFlags & HTTPCLIENT_FLAG_KEEP_ALIVE) == HTTPCLIENT_FLAG_KEEP_ALIVE) {
            if((pHTTPSession->HttpFlags & HTTPCLIENT_FLAG_USINGPROXY) != HTTPCLIENT_FLAG_USINGPROXY) {
                /* No proxy keep alive: */
                if((nRetCode = HTTPIntrnHeadersAdd(pHTTPSession,"Connection",10,"Keep-Alive",10))!= HTTPCLIENT_SUCCESS) {
                    break;
                }
            }
            else /* proxy keep alive */ {
                if((nRetCode = HTTPIntrnHeadersAdd(pHTTPSession,"Proxy-Connection",15,"Keep-Alive",10))!= HTTPCLIENT_SUCCESS) {
                    break;
                }
            }
        }

        /* If we know the total length in advance */
        if((pHTTPSession->HttpHeaders.HttpVerb == HTTPCLIENT_POST || pHTTPSession->HttpHeaders.HttpVerb == HTTPCLIENT_PUT) &&
			((pHTTPSession->HttpFlags & HTTPCLIENT_FLAG_SEND_CHUNKED) != HTTPCLIENT_FLAG_SEND_CHUNKED)) {
            /* set the total content length header */
            pHTTPSession->HttpHeadersInfo.nHTTPPostContentLength = TotalLength; /* Store for later usage */
            memset(ContentLength,0,32);
            IToA(ContentLength,TotalLength); /* Convert the buffer length to a string value */
			printf("Content-Length: %s[%d]", ContentLength,TotalLength);
            if((nRetCode = HTTPIntrnHeadersAdd(pHTTPSession,"Content-Length",14,ContentLength,strlen(ContentLength)))!= HTTPCLIENT_SUCCESS) {
                break;
            }
        }

        /* Add the "Cache control" header (if requested by the caller) */
        if((pHTTPSession->HttpFlags & HTTPCLIENT_FLAG_NO_CACHE) == HTTPCLIENT_FLAG_NO_CACHE) {
            if((nRetCode = HTTPIntrnHeadersAdd(pHTTPSession,"Cache-Control",13,"no-cache",8))!= HTTPCLIENT_SUCCESS) {
                break;
            }
        }


        /* Now we can connect to the remote server and send the leading request followed by the HTTP headers */
        /* Check for timeout */
        if(HTTPIntrnSessionEvalTimeout(pHTTPSession) == TRUE) {
            nRetCode = HTTPCLIENT_ERROR_SOCKET_TIME_OUT;
            break;
        }
        /*  Handle connection close message (reconnect) */
        if(pHTTPSession->HttpHeadersInfo.Connection == FALSE) {
            /* Gracefully close the connection and set the socket as invalid */
            if(pHTTPSession->HttpConnection.HttpSocket != HTTP_INVALID_SOCKET) {
                HTTPIntrnConnectionClose(pHTTPSession);
            }
            /* Connect to the remote server (or proxy) */
            nRetCode = HTTPIntrnConnectionOpen(pHTTPSession);
            if(nRetCode != HTTPCLIENT_SUCCESS) {
                break;
            }
        }

        /* Send the request along with the rest of the headers */
        if(pHTTPSession->HttpCredentials.CredAuthSchema != HTTPCLIENT_AUTHSCHEMA_NONE ||
            pHTTPSession->HttpProxy.ProxyAuthSchema != HTTPCLIENT_AUTHSCHEMA_NONE) /* If we have to authenticate we should use the HEAD verb */ {
            if(HTTPCLIENT_ALLOW_HEAD_VERB) /* HEAD should not be ussed if not defined */ {
                if((nRetCode = HTTPIntrnHeadersSend(pHTTPSession,HTTPCLIENT_HEAD)) != HTTPCLIENT_SUCCESS) {
                    break;
                }
                /* Set the state flag */
                pHTTPSession->HttpState = pHTTPSession->HttpState | HTTPCLIENT_STATE_HEAD_SENT;
            }
            else {
                /* Simply use the verb that was set by the caller without changing to HEAD */
                if((nRetCode = HTTPIntrnHeadersSend(pHTTPSession,pHTTPSession->HttpHeaders.HttpVerb)) != HTTPCLIENT_SUCCESS) {
                    break;
                }
                /* This the case where the caller know the total length to receive in advance */
                /* and he wishes to send the data right away */
                if((pHTTPSession->HttpHeaders.HttpVerb == HTTPCLIENT_POST || pHTTPSession->HttpHeaders.HttpVerb == HTTPCLIENT_PUT) &&
					((pHTTPSession->HttpFlags & HTTPCLIENT_FLAG_SEND_CHUNKED) != HTTPCLIENT_FLAG_SEND_CHUNKED)) {
                    /* Send the data */
                    nBytes = nDataLength;
                    if((nRetCode = HTTPIntrnSend(pHTTPSession,(CHAR*)pData,&nBytes)) != HTTPCLIENT_SUCCESS) {
                        break;
                    }
                    /* Set the session state */
                    pHTTPSession->HttpState = pHTTPSession->HttpState | HTTPCLIENT_STATE_POST_SENT;
                }
            }

            /* Retrive and analyze the Headers */
            if((nRetCode = HTTPIntrnHeadersReceive(pHTTPSession,nTimeout)) != HTTPCLIENT_SUCCESS) {
                break;
            }
            if(pHTTPSession->HttpHeadersInfo.nHTTPStatus != 401 && pHTTPSession->HttpHeadersInfo.nHTTPStatus != 407) {

                nRetCode = HTTPCLIENT_ERROR_AUTH_MISMATCH;
                break;
            }
            /* Authenticate */
            if((nRetCode = HTTPIntrnAuthenticate(pHTTPSession)) != HTTPCLIENT_SUCCESS) {
                break;
            }

        }
        else {
            /* No authentication use the verb that was requested by the caller */
            if((nRetCode = HTTPIntrnHeadersSend(pHTTPSession,pHTTPSession->HttpHeaders.HttpVerb)) != HTTPCLIENT_SUCCESS) {
                break;
            }
        }
        /* This the case where the caller know the total length to receive in advance */
        /* and he wishes to send the data right away */
        if((pHTTPSession->HttpHeaders.HttpVerb == HTTPCLIENT_POST || pHTTPSession->HttpHeaders.HttpVerb == HTTPCLIENT_PUT) &&
			((pHTTPSession->HttpFlags & HTTPCLIENT_FLAG_SEND_CHUNKED) != HTTPCLIENT_FLAG_SEND_CHUNKED)) {
            /* Send the data */
			/*[DX]Fix bug*/
            //nBytes = nDataLength;
			UINT32 written_len = 0;
			while (written_len < nDataLength) {
				nBytes = nDataLength - written_len;
				if((nRetCode = HTTPIntrnSend(pHTTPSession,(CHAR*)pData + written_len, &nBytes)) != HTTPCLIENT_SUCCESS) {
					break;
				}
				if (nBytes > 0) {
					written_len += nBytes;
					continue;
				}
				else {
					break;
				}
			}
			if (written_len == TotalLength) {
	            /* Set the session state */
	            pHTTPSession->HttpState = pHTTPSession->HttpState | HTTPCLIENT_STATE_POST_SENT;
			}
        }
    } while(0);

    return nRetCode;
}

HTTPCLIENT_RESULT httpclient_recv_response(HTTPCLIENT_SESSION_HANDLE pSession, UINT32 nTimeout)
{
    UINT32          nRetCode;
    P_HTTP_SESSION  pHTTPSession = NULL;


    // Cast the handle to our internal structure and check the pointers validity  */
    pHTTPSession = (P_HTTP_SESSION)pSession;
    if(!pHTTPSession) {
        return HTTPCLIENT_ERROR_INVALID_HANDLE;
    }

    do {
        if((nRetCode = HTTPIntrnHeadersReceive(pHTTPSession, nTimeout)) != HTTPCLIENT_SUCCESS) {
            break;
        }
    } while(0);

    return nRetCode;

}

HTTPCLIENT_RESULT httpclient_send_data(HTTPCLIENT_SESSION_HANDLE pSession,
		VOID *pBuffer, UINT32 nBufferLength, UINT32 nTimeout)
{
    UINT32          nRetCode     = HTTPCLIENT_SUCCESS;
    UINT32          nBytes;
    CHAR            Chunk[HTTPCLIENT_MAX_CHUNK_HEADER];
    P_HTTP_SESSION  pHTTPSession = NULL;


    /* Cast the handle to our internal structure and check the pointer validity */
    pHTTPSession = (P_HTTP_SESSION)pSession;
    do {
        if(!pHTTPSession) {
            nRetCode =  HTTPCLIENT_ERROR_INVALID_HANDLE;
            break;
        }

        /* Set the operation timeout counters */
        pHTTPSession->HttpCounters.nActionStartTime = HTTPIntrnSessionGetUpTime();
        pHTTPSession->HttpCounters.nActionTimeout = HTTP_TIMEOUT(nTimeout);

        /* Did the caller specified chunked sending? */
        if((pHTTPSession->HttpFlags & HTTPCLIENT_FLAG_SEND_CHUNKED) == HTTPCLIENT_FLAG_SEND_CHUNKED) {
            /* Prep the chunk Header and send it */
            memset(Chunk,0x00,HTTPCLIENT_MAX_CHUNK_HEADER);
            HTTPStrLToH(Chunk,nBufferLength);
            strcat(Chunk,HTTPCLIENT_CRLF);

            /* Send the leading CrLf (only after the first chunk) */
            if(pHTTPSession->HttpCounters.nSentChunks >= 1) {
                nBytes = 2;;
                nRetCode = HTTPIntrnSend(pHTTPSession,HTTPCLIENT_CRLF,&nBytes);
                if(nRetCode != HTTPCLIENT_SUCCESS) {
                    break;
                }
            }

            /* Send the chunk header */
            nBytes = strlen(Chunk);
            /* Send the data */
			/*[DX]Fix bug*/
            //nBytes = nDataLength;
			UINT32 written_len = 0;
			while (written_len < strlen(Chunk)) {
				if((nRetCode = HTTPIntrnSend(pHTTPSession, Chunk + written_len, &nBytes)) != HTTPCLIENT_SUCCESS) {
					break;
				}
				if (nBytes > 0) {
					written_len += nBytes;
					continue;
				} else {
					break;
				}
			}
			if (written_len < strlen(Chunk)) {
				nRetCode = HTTPCLIENT_ERROR_SOCKET_SEND;
                break;
            }
        }

		if(pBuffer && nBufferLength > 0) {
	        /* Send the data */
			/*[DX]Fix bug*/
			UINT32 written_len = 0;
			while (written_len < nBufferLength) {
				nBytes = nBufferLength - written_len;
				if((nRetCode = HTTPIntrnSend(pHTTPSession,(CHAR*)pBuffer + written_len, &nBytes)) != HTTPCLIENT_SUCCESS) {
					break;
				}
				if (nBytes > 0) {
					written_len += nBytes;
					continue;
				}
				else {
					break;
				}
			}
			if (written_len < nBufferLength) {
				printf("[ERROR]written_len [%d] but nBufferLength[%d].\n", written_len, nBufferLength);
				nRetCode = HTTPCLIENT_ERROR_SOCKET_SEND;
                break;
            }

			//nBytes = nBufferLength;
	        //nRetCode = HTTPIntrnSend(pHTTPSession,(CHAR*)pBuffer,&nBytes);
	        //if(nRetCode != HTTPCLIENT_SUCCESS) {
	        //    break;
	        //}

			#if 1
			if((pHTTPSession->HttpFlags & HTTPCLIENT_FLAG_SEND_CHUNKED) == HTTPCLIENT_FLAG_SEND_CHUNKED) {
				/* Set the chunks count */
				pHTTPSession->HttpCounters.nSentChunks++;
			}
			#endif
		}

		#if 1
		if(nBufferLength == 0) {
			/* If we are using chunks then.. */
			if((pHTTPSession->HttpFlags & HTTPCLIENT_FLAG_SEND_CHUNKED) == HTTPCLIENT_FLAG_SEND_CHUNKED) {
				/* If it was the last chunk (0) we should re-get the headers from the server reply */
				/* Send the trailing CrLf */
				nBytes = 2;;
				nRetCode = HTTPIntrnSend(pHTTPSession,HTTPCLIENT_CRLF,&nBytes);
				if(nRetCode != HTTPCLIENT_SUCCESS) {
					break;
				}
				/* Get the remote headers (since the last chunk was transmitted we can expect the server to start the reply) */
				if((nRetCode  = HTTPIntrnHeadersReceive(pHTTPSession,nTimeout)) != HTTPCLIENT_SUCCESS) {
					break;
				}
			}
			else {
	            /* Set the session state */
	            pHTTPSession->HttpState = pHTTPSession->HttpState | HTTPCLIENT_STATE_POST_SENT;
			}
		}
		#else
        /* If we are using chunks then.. */
        if((pHTTPSession->HttpFlags & HTTPCLIENT_FLAG_SEND_CHUNKED) == HTTPCLIENT_FLAG_SEND_CHUNKED) {
            /* If it was the last chunk (0) we should re-get the headers from the server reply */
            if(nBufferLength == 0) {
                /* Send the trailing CrLf */
                nBytes = 2;;
                nRetCode = HTTPIntrnSend(pHTTPSession,HTTPCLIENT_CRLF,&nBytes);
                if(nRetCode != HTTPCLIENT_SUCCESS) {
                    break;
                }
                /* Get the remote headers (since the last chunk was transmitted we can expect the server to start the reply) */
                if((nRetCode  = HTTPIntrnHeadersReceive(pHTTPSession,nTimeout)) != HTTPCLIENT_SUCCESS) {
                    break;
                }
            }
			else {
	            /* Set the chunks count */
	            pHTTPSession->HttpCounters.nSentChunks++;
			}
        }
		#endif

    } while(0);

    return nRetCode;
}

HTTPCLIENT_RESULT httpclient_receive_data(HTTPCLIENT_SESSION_HANDLE pSession,
		VOID *pBuffer, UINT32 nBytesToRead, UINT32 nTimeout, UINT32 *nBytesRecived)
{
    UINT32          nBytes          = 0;
    UINT32          nRetCode        = 0 ;
    INT32           nProjectedBytes = 0; /* Should support negative numbers */
    CHAR            *pNullPtr;
    BOOL            EndOfStream = FALSE;
    P_HTTP_SESSION  pHTTPSession = NULL;


    /* Cast the handle to our internal structure and check the pointers validity */
    pHTTPSession = (P_HTTP_SESSION)pSession;
    if(!pHTTPSession) {
        return HTTPCLIENT_ERROR_INVALID_HANDLE;
    }

    /* If the last verb that was used was HEAD there is no point to get this data (chanses are that we will endup with timeout) */
    if(pHTTPSession->HttpHeaders.HttpVerb == HTTPCLIENT_HEAD) {
        return HTTPCLIENT_EOS;

    }

    /* Set the operation timeout counters */
    pHTTPSession->HttpCounters.nActionStartTime = HTTPIntrnSessionGetUpTime();
    pHTTPSession->HttpCounters.nActionTimeout = HTTP_TIMEOUT(nTimeout);


    nBytes              = nBytesToRead - 1; /* We will spare 1 byte for the trailing null termination */
    *((CHAR*)pBuffer)   = 0;                /* Null terminate the user supplied buffer */
    *(nBytesRecived)    = 0;                /* Set the return bytes count to 0 */

    /* We can read the data only if we got valid headers (and not authentication requests for example) */
    if((pHTTPSession->HttpState & HTTPCLIENT_STATE_HEADERS_PARSED) != HTTPCLIENT_STATE_HEADERS_PARSED) {
        return HTTPCLIENT_ERROR_BAD_STATE;
    }

    /* Is it a chunked mode transfer? */
    if((pHTTPSession->HttpFlags & HTTPCLIENT_FLAG_CHUNKED) == HTTPCLIENT_FLAG_CHUNKED) {

        /* How many bytes left until the next chunk? */
        if(pHTTPSession->HttpCounters.nBytesToNextChunk == 0) {
            /* Read the chunk header and get its length */
            if(HTTPIntrnGetRemoteChunkLength(pHTTPSession) != HTTPCLIENT_SUCCESS) {
                /* Could not parse the chunk parameter */
                return HTTPCLIENT_ERROR_CHUNK;
            }

            /* 0 Bytes chunk, we should return end of stream */
            if(pHTTPSession->HttpCounters.nRecivedChunkLength == 0) {
                return HTTPCLIENT_EOS;
            }
        }
        /* If we are about to read pass the next chunk, reduce the read bytes so we will read */
        /* non HTML data */
        nProjectedBytes = pHTTPSession->HttpCounters.nBytesToNextChunk - nBytes;
        if ( nProjectedBytes <= 0) {
            /* Set the correct bytes count we should read */
            nBytes  = pHTTPSession->HttpCounters.nBytesToNextChunk;
        }
    }

    /* Do we have the content length? */
    if(pHTTPSession->HttpHeadersInfo.nHTTPContentLength > 0) {
        /* Length of the projected buffer */
        nProjectedBytes = pHTTPSession->HttpCounters.nRecivedBodyLength +  nBytes;
        /* If we are going to read more then the known content length then.. */
        if(nProjectedBytes >= (INT32)pHTTPSession->HttpHeadersInfo.nHTTPContentLength)  {
            /* Reduce the received bytes count to the correct size */
            nBytes = pHTTPSession->HttpHeadersInfo.nHTTPContentLength - pHTTPSession->HttpCounters.nRecivedBodyLength;

        }
    }
    /* Receive the data from the socket */
    nRetCode = HTTPIntrnRecv(pHTTPSession,(CHAR*)pBuffer,&nBytes,FALSE);
    /* Set the return bytes count */
    *(nBytesRecived) = nBytes;   /* + 1; Fixed 11/9/2005 */

    /* Pointer to the end of the buffer */
    pNullPtr    = (CHAR*)pBuffer + nBytes ;
    /* And null terminate */
    *pNullPtr = 0;

    /* Socket read went OK */
    if(nRetCode == HTTPCLIENT_SUCCESS) {

#if defined(_HTTP_DEBUGGING_)
        if(pHTTPSession->pDebug) {
            pHTTPSession->pDebug("httpclient_receive_data",NULL,0,"Reading %d bytes",nBytes);
        }
#endif
        /* Set the HTTP counters */
        pHTTPSession->HttpCounters.nRecivedBodyLength += nBytes;
        /* If we know the total content length and.. */
        if(pHTTPSession->HttpHeadersInfo.nHTTPContentLength > 0) {
            /* If total received body is equal or greater then the known content length then.. */
            if( pHTTPSession->HttpCounters.nRecivedBodyLength >= pHTTPSession->HttpHeadersInfo.nHTTPContentLength) {
                /* Raise a flag to signal end of stream */
                EndOfStream = TRUE;
            }
        }
        /* Is it a chunked mode transfer? */
        if((pHTTPSession->HttpFlags & HTTPCLIENT_FLAG_CHUNKED) == HTTPCLIENT_FLAG_CHUNKED) {
            /* We are a little closer to the next chunk now */
            pHTTPSession->HttpCounters.nBytesToNextChunk -= nBytes;
        }
        /* Is it End of stream? */
        if(EndOfStream == TRUE) {
            /* So exit */
            return HTTPCLIENT_EOS;
        }
    }

    return nRetCode ;
}

HTTPCLIENT_RESULT httpclient_get_info(HTTPCLIENT_SESSION_HANDLE pSession, httpclient_t *pHTTPClient)
{
    P_HTTP_SESSION  pHTTPSession = NULL;


    /* Cast the handle to our internal structure and check the pointers validity */
    pHTTPSession = (P_HTTP_SESSION)pSession;
    if(!pHTTPSession)
    {
        return HTTPCLIENT_ERROR_INVALID_HANDLE;
    }
    /* Reset the users info structure */
    memset(pHTTPClient,0x00,sizeof(httpclient_t));

    pHTTPClient->HTTPStatusCode              = pHTTPSession->HttpHeadersInfo.nHTTPStatus;
    pHTTPClient->RequestBodyLengthSent       = pHTTPSession->HttpCounters.nSentBodyBytes;
    pHTTPClient->ResponseBodyLengthReceived  = pHTTPSession->HttpCounters.nRecivedBodyLength;
    pHTTPClient->TotalResponseBodyLength     = pHTTPSession->HttpHeadersInfo.nHTTPContentLength;
    pHTTPClient->HttpState                   = pHTTPSession->HttpState;

    return HTTPCLIENT_SUCCESS;
}

#if 0
/**
 * \brief Http Session FirstHeader Get.
 *
 * \details Initiate the headr searching functions.
 *
 * \param pSession   [in]HTTP Session Handle.
 * \param pSearchClue [in]
 * \param pHeaderBuffer [in].
 *
 * \return Please refer to #HTTPCLIENT_RESULT.
 */
HTTPCLIENT_RESULT HTTPClientFindFirstHeader(HTTPCLIENT_SESSION_HANDLE pSession,
		CHAR *pSearchClue,CHAR *pHeaderBuffer, UINT32 *nLength)
{
    P_HTTP_SESSION  pHTTPSession = NULL;
    UINT32         nClueLength;


    /* Cast the handle to our internal structure and check the pointers validity */
    pHTTPSession = (P_HTTP_SESSION)pSession;
    if(!pHTTPSession) {
        return HTTPCLIENT_ERROR_INVALID_HANDLE;
    }

    nClueLength = strlen(pSearchClue); /* See if we are not to long */
    if(nClueLength >= HTTPCLIENT_MAX_HEADER_SEARCH_CLUE) {
        return HTTPCLIENT_ERROR_HEADER_BIG_CLUE;
    }
    else {
        strcpy(pHTTPSession->HttpHeaders.SearchClue,pSearchClue);
        pHTTPSession->HttpHeaders.HeaderSearch.nLength = 0;
        pHTTPSession->HttpHeaders.HeaderSearch.pParam = NULL;
    }

    return HTTPCLIENT_SUCCESS;
}


///////////////////////////////////////////////////////////////////////////////
//
// Function     : HTTPClientFindCloseHeader
// Purpose      : Terminate a headers search session
// Returns      : HTTP Status
// Last updated : 01/09/2005
//
///////////////////////////////////////////////////////////////////////////////

HTTPCLIENT_RESULT HTTPClientFindCloseHeader (HTTPCLIENT_SESSION_HANDLE pSession)
{
    P_HTTP_SESSION  pHTTPSession = NULL;

    // Cast the handle to our internal structure and check the pointers validity */
    pHTTPSession = (P_HTTP_SESSION)pSession;
    if(!pHTTPSession)
    {
        return HTTPCLIENT_ERROR_INVALID_HANDLE;
    }

    pHTTPSession->HttpHeaders.SearchClue[0] = 0;
    pHTTPSession->HttpHeaders.HeaderSearch.nLength = 0;
    pHTTPSession->HttpHeaders.HeaderSearch.pParam = NULL;

    return HTTPCLIENT_SUCCESS;

}

///////////////////////////////////////////////////////////////////////////////
//
// Function     : HTTPClientGetNextHeader
// Purpose      : Terminate a headers search session
// Returns      : HTTP Status
// Last updated : 01/09/2005
//
///////////////////////////////////////////////////////////////////////////////
HTTPCLIENT_RESULT HTTPClientGetNextHeader (HTTPCLIENT_SESSION_HANDLE pSession, CHAR *pHeaderBuffer, UINT32 *nLength)
{
    P_HTTP_SESSION  pHTTPSession = NULL;
    UINT32          nOffset = 0;
    UINT32          nRetCode;
    httpclientParam_t  HttpHeader;
    CHAR            *pPtr;


    // Cast the handle to our internal structure and check the pointers validity */
    pHTTPSession = (P_HTTP_SESSION)pSession;
    if(!pHTTPSession)
    {
        return HTTPCLIENT_ERROR_INVALID_HANDLE;
    }
    if(pHTTPSession->HttpHeaders.HeaderSearch.nLength > 0) // We must adjust the search offset since it is not the fierst iteration */
    {
        nOffset = pHTTPSession->HttpHeaders.HeaderSearch.pParam - pHTTPSession->HttpHeaders.HeadersIn.pParam;
    }
    // Search for the next header
    nRetCode = HTTPIntrnHeadersFind(pHTTPSession,pHTTPSession->HttpHeaders.SearchClue,&HttpHeader,TRUE,nOffset);

    if(nRetCode == HTTPCLIENT_SUCCESS)
    {
        if(HttpHeader.nLength > *(nLength)) // Check for sufficiant length
        {
            *(nLength) = HttpHeader.nLength;
            pHeaderBuffer[0] = 0; // Reset the users buffer
            return HTTPCLIENT_ERROR_NO_MEMORY;
        }

        pPtr = HttpHeader.pParam;
        nOffset = 0;
        if(*pPtr == 0x0d)
        {
            nOffset++;
            pPtr++;
        }
        if(*pPtr == 0x0a)
        {
            nOffset++;
            pPtr++;
        }

        strncpy(pHeaderBuffer,pPtr,HttpHeader.nLength - nOffset);
        pHeaderBuffer[HttpHeader.nLength - nOffset] = 0;
        *(nLength) = HttpHeader.nLength - nOffset;
        pHTTPSession->HttpHeaders.HeaderSearch.pParam = HttpHeader.pParam + HttpHeader.nLength;
        pHTTPSession->HttpHeaders.HeaderSearch.nLength++;

        return HTTPCLIENT_SUCCESS;

    }
    return nRetCode;
}
#endif

#if defined(_HTTP_DEBUGGING_)
/**
 * \brief HTTP Session Close.
 *
 * \details Sets the HTTP debug function pointer.
 *
 * \param pSession[in]   HTTP Session Handle.
 * \param pDebugFunc[in] Function pointer to the the caller debugging function.
 *
 * \return Please refer to #HTTPCLIENT_RESULT.
 */
HTTPCLIENT_RESULT httpclient_set_debughook(HTTPCLIENT_SESSION_HANDLE pSession, E_HTTPDebug *pDebugFunc)
{

    P_HTTP_SESSION pHTTPSession = NULL;

    /* Cast the handle to our internal structure and check the pointers validity first */
    pHTTPSession = (P_HTTP_SESSION)pSession;
    if (!pHTTPSession) {
        return HTTPCLIENT_ERROR_INVALID_HANDLE;
    }

    pHTTPSession->pDebug =  pDebugFunc;
    if (pHTTPSession->pDebug) {
        pHTTPSession->pDebug("httpclient_set_debughook", NULL, 0, "Debughook set, return code is %d", HTTPCLIENT_SUCCESS);
    }

    return HTTPCLIENT_SUCCESS;
}
#endif

///////////////////////////////////////////////////////////////////////////////
//
// Function     : HTTPIntrnSetURL
// Purpose      : Parse the user's URL
// Returns      : HTTP Status
// Last updated : 01/09/2005
//
///////////////////////////////////////////////////////////////////////////////

static UINT32 HTTPIntrnSetURL (P_HTTP_SESSION pHTTPSession,
                        CHAR *pUrl,         // [IN] a null terminated string containing the Url we should retrieve
                        UINT32 nUrlLength)  // [IN] The length the Url string
{

    UINT32         nUrlOffset;      // Offset in bytes within the Url string
    httpclientUrl_t       *pUrlPtr;        // a Pointer to the Url structure (within the global session structure)
    CHAR           *pPtr;           // a Pointer for the Url port (Used in the parsing process)
    CHAR           UrlPort[16];     // a temporary byte array for the Url port conversion operation (string to number)

    // Check for the session pointer validity
    if(!pHTTPSession)
    {
        return HTTPCLIENT_ERROR_INVALID_HANDLE;
    }


    // Get the length of the Url
    nUrlLength = strlen(pUrl);

    // Check if it is not longer than the permitted length
    if((nUrlLength + 1) > HTTPCLIENT_MAX_URL_LENGTH)
    {
        return HTTPCLIENT_ERROR_LONG_INPUT;
    }

    // Point the local pointer on the global structure
    pUrlPtr = &pHTTPSession->HttpUrl;

    // Copy to the internal buffer
    memcpy(pHTTPSession->HttpUrl.Url,pUrl,nUrlLength);
    nUrlOffset              = 0;

    // Get the Url base ("http" or "https")
    if(HTTPStrSearch(pUrlPtr->Url,":",nUrlOffset,nUrlLength,&pUrlPtr->UrlBsee) == FALSE)
    {
        return HTTPCLIENT_ERROR_BAD_URL;
    }
    // Increase the offset parameter
    nUrlOffset += pUrlPtr->UrlBsee.nLength;
    // If we can parse the string "HTTPS" we can assume a secured session
    if(HTTPStrInsensitiveCompare(pUrlPtr->UrlBsee.pParam,"https",pUrlPtr->UrlBsee.nLength) == TRUE)
    {
        // Set the secured flags on the session
        pHTTPSession->HttpFlags = pHTTPSession->HttpFlags | HTTPCLIENT_FLAG_URLHTTPS;
        pHTTPSession->HttpFlags = pHTTPSession->HttpFlags | HTTPCLIENT_FLAG_SECURE;
        // ToDo: Init TLS (GetProtocol)


#if defined(_HTTP_DEBUGGING_)
        if(pHTTPSession->pDebug)
        {
            pHTTPSession->pDebug("HTTPIntrnSetURL",NULL,0,"SSL Protected Url %s",pUrl);
        }
#endif
    }
    else // it should be "http"
    {
        if(HTTPStrInsensitiveCompare(pUrlPtr->UrlBsee.pParam,"http",pUrlPtr->UrlBsee.nLength) == FALSE)
        {
            return HTTPCLIENT_ERROR_BAD_URL; // cOULD NOT DETECT http or https prefix

        }
    }
    // Look for standard Url elements
    if(HTTPStrSearch(pUrlPtr->Url,"://",nUrlOffset,3,0) == FALSE)
    {
        return HTTPCLIENT_ERROR_BAD_URL; // Could not detect "://"
    }
    // Increase the offset parameter
    nUrlOffset += 3;

    // Get the host name
    if(HTTPStrSearch(pUrlPtr->Url,"/",nUrlOffset,(nUrlLength - nUrlOffset),&pUrlPtr->UrlHost) == FALSE)
    {
        pUrlPtr->Url[nUrlLength] = '/';
        nUrlLength++;
        if(HTTPStrSearch(pUrlPtr->Url,"/",nUrlOffset,(nUrlLength - nUrlOffset),&pUrlPtr->UrlHost) == FALSE)
        {
            return HTTPCLIENT_ERROR_BAD_URL;
        }
    }

    nUrlOffset += pUrlPtr->UrlHost.nLength;

    // Do we have the port within the hostname?
    if(HTTPStrSearch(pUrlPtr->Url,":",
        (nUrlOffset - pUrlPtr->UrlHost.nLength),
        pUrlPtr->UrlHost.nLength,
        &pUrlPtr->UrlPort) == TRUE)
    {
        if((pUrlPtr->UrlHost.nLength - pUrlPtr->UrlPort.nLength) < 10)
        {
            // To-Do: check the actual port length before the memcpy
            pUrlPtr->UrlPort.pParam += pUrlPtr->UrlPort.nLength + 1;
            memcpy(UrlPort,pUrlPtr->UrlPort.pParam,15);
            pUrlPtr->UrlPort.nLength = 0;
            pPtr = UrlPort;
            while(*pPtr && pPtr++)
            {

                pUrlPtr->UrlPort.nLength++;
                if(*pPtr == '/')
                {
                    *pPtr = 0;
                    pUrlPtr->nPort = (UINT16)atol(UrlPort);
                    pHTTPSession->HttpFlags = pHTTPSession->HttpFlags | HTTPCLIENT_FLAG_URLANDPORT;
                    break;
                }
            }
        }
        else
        {
            // Port too big
            return HTTPCLIENT_ERROR_BAD_URL;
        }
    }

    // Get the request body
    pUrlPtr->UrlRequest.pParam  = pUrlPtr->Url + nUrlOffset;
    pUrlPtr->UrlRequest.nLength = nUrlLength - nUrlOffset;

    // If we got SSL url with no port we should set the default ssl port
    if((pHTTPSession->HttpFlags & HTTPCLIENT_FLAG_URLHTTPS) == HTTPCLIENT_FLAG_URLHTTPS)
    {
        if((pHTTPSession->HttpFlags & HTTPCLIENT_FLAG_URLANDPORT) != HTTPCLIENT_FLAG_URLANDPORT)
        {
            pHTTPSession->HttpUrl.nPort = HTTPCLIENT_DEFAULT_SSL_PORT;

        }

    }

    // Set the state flag
    pHTTPSession->HttpState  = pHTTPSession->HttpState | HTTPCLIENT_STATE_URL_PARSED;

    return HTTPCLIENT_SUCCESS;

}


///////////////////////////////////////////////////////////////////////////////
//
// Function     : HTTPIntrnResizeBuffer
// Purpose      : Resize the HTTP headers buffer and reset the pointers
// Returns      : HTTP Status
// Last updated : 01/09/2005
//
///////////////////////////////////////////////////////////////////////////////

static UINT32 HTTPIntrnResizeBuffer (P_HTTP_SESSION pHTTPSession,
                              UINT32 nNewBufferSize)    // [IN] The new (and larger) buffer size
{

    CHAR                *pPtr           = NULL;
    UINT32              nCurrentBufferSize;

    if(!pHTTPSession)
    {
        return HTTPCLIENT_ERROR_INVALID_HANDLE;
    }
    // If the new buffer size is less or equal to the current buffer size then..
    if(pHTTPSession->HttpHeaders.HeadersBuffer.nLength >= nNewBufferSize)
    {

        // Return an error (bad buffer)
        return HTTPCLIENT_ERROR_BUFFER_RSIZE;
    }

    // If the new buffer size is bigger then the defined maximum buffer size then..
    if(nNewBufferSize > HTTPCLIENT_MAX_SEND_RECV_HEADERS)
    {
        // Return an error (bad buffer)
        return HTTPCLIENT_ERROR_BUFFER_RSIZE;
    }
    // Current buffer size is the sum of the incoming and outgoing headers strings lengths
    nCurrentBufferSize = pHTTPSession->HttpHeaders.HeadersOut.nLength + pHTTPSession->HttpHeaders.HeadersIn.nLength;
    // Allocate a new buffer with the requested buffer size
    #if defined(__HARMONY_OS__)
    pPtr = (CHAR*)malloc(nNewBufferSize);
    #else
    pPtr = (CHAR*)malloc(ALIGN(nNewBufferSize));
    #endif
    if(!pPtr)
    {
        // malloc() error
        return HTTPCLIENT_ERROR_NO_MEMORY;
    }
    // Copy the memory only if there is data to copy
    if(nCurrentBufferSize > 0)
    {
        memcpy(pPtr,pHTTPSession->HttpHeaders.HeadersBuffer.pParam,nCurrentBufferSize);
        // Reset the rest of the buffer
        memset(pPtr + nCurrentBufferSize, 0x00,(nNewBufferSize - nCurrentBufferSize));
    }
    else
    {
        // Reset the entire buffer (no previous buffer was copied)
        memset(pPtr,0x00,nNewBufferSize);
    }

    free(pHTTPSession->HttpHeaders.HeadersBuffer.pParam);

    pHTTPSession->HttpHeaders.HeadersBuffer.pParam = pPtr;
    pHTTPSession->HttpHeaders.HeadersBuffer.nLength = nNewBufferSize;

    // Refresh the pointers
    pHTTPSession->HttpHeaders.HeadersOut.pParam = pHTTPSession->HttpHeaders.HeadersBuffer.pParam;
    if(pHTTPSession->HttpHeaders.HeadersIn.pParam)
    {
        pHTTPSession->HttpHeaders.HeadersIn.pParam  =pHTTPSession->HttpHeaders.HeadersBuffer.pParam + pHTTPSession->HttpHeaders.HeadersOut.nLength;
    }
    return HTTPCLIENT_SUCCESS;
}

///////////////////////////////////////////////////////////////////////////////
//
// Function     : HTTPIntrnRemoveHeader
// Purpose      : Removes an HTTP headers by its name
// Last updated : 01/09/2005
//
///////////////////////////////////////////////////////////////////////////////

static UINT32 HTTPIntrnHeadersRemove (P_HTTP_SESSION pHTTPSession,
                               CHAR *pHeaderName)   // [IN] The header's name

{

    httpclientParam_t  HttpParam;
    UINT32      nRetCode = HTTPCLIENT_SUCCESS;
    UINT32      nBytes;

    if(!pHTTPSession) // Pointer validation check
    {
        return HTTPCLIENT_ERROR_INVALID_HANDLE;
    }

    // First see if we have that header in our outgoing headers buffer
    do
    {

        if((nRetCode = HTTPIntrnHeadersFind(pHTTPSession,pHeaderName,&HttpParam,FALSE,0)) != HTTPCLIENT_SUCCESS)
        {
            // Could not find this header
            break;
        }
        // Calculate the new headers length
        nBytes = (HttpParam.pParam - pHTTPSession->HttpHeaders.HeadersOut.pParam);
        nBytes -= HttpParam.nLength;


        // Copy the memory
        memcpy(HttpParam.pParam, HttpParam.pParam + HttpParam.nLength,nBytes);

        // Set the new length
        pHTTPSession->HttpHeaders.HeadersOut.nLength -= HttpParam.nLength;

        // Reset the buffer from it's modified end to it's previous end
        memset(pHTTPSession->HttpHeaders.HeadersOut.pParam + pHTTPSession->HttpHeaders.HeadersOut.nLength,0x00,HttpParam.nLength);

    } while(0);

#if defined(_HTTP_DEBUGGING_)
    if(pHTTPSession->pDebug)
    {
        pHTTPSession->pDebug("HTTPIntrnHeadersRemove",NULL,0,"Removing Header %",pHeaderName);
    }
#endif


    return nRetCode;

}


///////////////////////////////////////////////////////////////////////////////
//
// Function     : HTTPIntrnHeadersAdd
// Purpose      : Add HTTP headers to the outgoing headers buffers
// Last updated : 01/09/2005
//
///////////////////////////////////////////////////////////////////////////////

static UINT32 HTTPIntrnHeadersAdd (P_HTTP_SESSION pHTTPSession,
                            CHAR *pHeaderName,   // [IN] The header's name
                            UINT32 nNameLength,  // [IN] Name length
                            CHAR *pHeaderData,   // [IN] The Header's data
                            UINT32 nDataLength)  // [IN] Data length
{
    CHAR            *pPtr;
    UINT32          nProjectedHeaderLength;
    UINT32          nProjectedBufferLength;
    INT32           nCurrentfreeSpace;
    INT32           nProjectedfreeSpace;
    UINT32          nRetCode;

    if(!pHTTPSession) // pointer validation check
    {
        return HTTPCLIENT_ERROR_INVALID_HANDLE;
    }

    nProjectedHeaderLength  = nNameLength + nDataLength + 4;
    nProjectedBufferLength  = nProjectedHeaderLength + pHTTPSession->HttpHeaders.HeadersOut.nLength + pHTTPSession->HttpHeaders.HeadersIn.nLength;
    nCurrentfreeSpace       = pHTTPSession->HttpHeaders.HeadersBuffer.nLength - (pHTTPSession->HttpHeaders.HeadersOut.nLength + pHTTPSession->HttpHeaders.HeadersIn.nLength);
    nProjectedfreeSpace     = nCurrentfreeSpace - nProjectedHeaderLength;

    // Check total size limit
    if(nProjectedBufferLength > HTTPCLIENT_MAX_SEND_RECV_HEADERS)
    {
        return HTTPCLIENT_ERROR_NO_MEMORY;
    }

    if((INT32)nProjectedfreeSpace < 0)
    {
        if(HTTPCLIENT_MEMORY_RESIZABLE == FALSE)
        {
            // Need more space but we can't grow beyond the current size
            return HTTPCLIENT_ERROR_NO_MEMORY;
        }
        else
        {
            // We can resizes so..
            nRetCode = HTTPIntrnResizeBuffer(pHTTPSession,nProjectedBufferLength + HTTPCLIENT_MEMORY_RESIZE_FACTOR);
            if(nRetCode != HTTPCLIENT_SUCCESS)
            {
                return nRetCode;
            }
        }
    }

    // Move the incoming headers data within the buffer so we will have space for the added headers
    if(pHTTPSession->HttpHeaders.HeadersIn.pParam)
    {
        // Move the data and reset the data in the offset.
        memcpy(pHTTPSession->HttpHeaders.HeadersIn.pParam + nProjectedHeaderLength  ,
            pHTTPSession->HttpHeaders.HeadersIn.pParam,
            pHTTPSession->HttpHeaders.HeadersIn.nLength);
        // Reset the space created
        memset(pHTTPSession->HttpHeaders.HeadersOut.pParam + pHTTPSession->HttpHeaders.HeadersOut.nLength,
            0x00,
            nProjectedHeaderLength);

    }

    pPtr = pHTTPSession->HttpHeaders.HeadersOut.pParam + pHTTPSession->HttpHeaders.HeadersOut.nLength;
    // Create the new header
    memcpy(pPtr,pHeaderName,nNameLength);
    pPtr += nNameLength;
    memcpy(pPtr,": ",2);
    pPtr += 2;
    memcpy(pPtr,pHeaderData,nDataLength);
    pPtr += nDataLength;
    memcpy(pPtr,HTTPCLIENT_CRLF,2);
    pPtr += 2;

    // Set the new length
    pHTTPSession->HttpHeaders.HeadersOut.nLength += nProjectedHeaderLength;

#if defined(_HTTP_DEBUGGING_)
    if(pHTTPSession->pDebug)
    {
        pHTTPSession->pDebug("HTTPIntrnHeadersAdd",NULL,0,"Adding Header %s: %s",pHeaderName,pHeaderData);
    }
#endif

    return HTTPCLIENT_SUCCESS;
}

///////////////////////////////////////////////////////////////////////////////
//
// Function     : HTTPIntrnConnectionClose
// Purpose      : Closes an active socket connection and invalidate the socket handle
// Returns      : HTTP Status
// Last updated : 01/09/2005
//
///////////////////////////////////////////////////////////////////////////////

static UINT32 HTTPIntrnConnectionClose (P_HTTP_SESSION pHTTPSession)
{

    INT32  nRetCode = HTTPCLIENT_SUCCESS;

    do
    {
        if(!pHTTPSession)   // Validate the session pointer
        {
            nRetCode =  HTTPCLIENT_ERROR_INVALID_HANDLE;
            break;
        }
		/* [DX]For tls */
		if((pHTTPSession->HttpFlags & HTTPCLIENT_FLAG_SECURE) == HTTPCLIENT_FLAG_SECURE)
		{
			// TLS Close
			nRetCode = HTTPWrapperSSLClose(pHTTPSession->HttpConnection.TlsDataParams);
			break;
		}

        // If we have a valid socket then..
        if(pHTTPSession->HttpConnection.HttpSocket != HTTP_INVALID_SOCKET)// INVALID_SOCKET
        {
            if((pHTTPSession->HttpFlags & HTTPCLIENT_FLAG_SECURE) == HTTPCLIENT_FLAG_SECURE)
            {
                // TLS Close
                nRetCode = HTTPWrapperSSLClose(pHTTPSession->HttpConnection.HttpSocket);
            }

            // Gracefully close it
            shutdown(pHTTPSession->HttpConnection.HttpSocket,0x01);
#ifdef _WIN32
            closesocket(pHTTPSession->HttpConnection.HttpSocket);
#else
            close(pHTTPSession->HttpConnection.HttpSocket);
#endif
            // And invalidate the socket
            pHTTPSession->HttpConnection.HttpSocket = HTTP_INVALID_SOCKET;

            break;;
        }
        else
        {
            // Not a valid socket error
            nRetCode = HTTPCLIENT_ERROR_SOCKET_INVALID;
            break;
        }

    } while(0);

#if defined(_HTTP_DEBUGGING_)
    if(pHTTPSession->pDebug)
    {
        pHTTPSession->pDebug("HTTPIntrnConnectionClose",NULL,0,"");
    }
#endif
    return nRetCode;
}

///////////////////////////////////////////////////////////////////////////////
//
// Function     : HTTPIntrnConnectionOpen
// Purpose      : Opens a socket connection to the remote host or proxy server
// Returns      : HTTP Status
// Last updated : 01/09/2005
//
///////////////////////////////////////////////////////////////////////////////

static UINT32 HTTPIntrnConnectionOpen (P_HTTP_SESSION pHTTPSession)
{
	INT32            nRetCode = HTTPCLIENT_SUCCESS;     // a function return code value
	UINT32           nNullOffset = 0;                        // a helper value to null terminate a given string
	int              nNonBlocking    = 1;                // non blocking mode parameter
	CHAR             Backup = 0;                             // a container for a char value (helps in temporary null termination)
	// HTTP_HOSTNET     *HostEntry;                          // Socket host entry pointer
	UINT32           Address = 0;
	HTTP_SOCKADDR_IN ServerAddress;                      // Socket address structure
	HTTP_SOCKADDR_IN LoaclAddress;                       // Socket address structure (for client binding)
	do
	{

		if(!pHTTPSession)
		{
			nRetCode  =  HTTPCLIENT_ERROR_INVALID_HANDLE;
			break;
		}
		// Use an existing connection if valid
		if(pHTTPSession->HttpConnection.HttpSocket != HTTP_INVALID_SOCKET)
		{
			// Set the state flag
			pHTTPSession->HttpState  = pHTTPSession->HttpState | HTTPCLIENT_STATE_HOST_CONNECTED;
			return HTTPCLIENT_SUCCESS;
		}
		/* [DX]For tls */
		if((pHTTPSession->HttpFlags & HTTPCLIENT_FLAG_SECURE) == HTTPCLIENT_FLAG_SECURE)
		{ // Is it a TLS connection?

			// Resolve the target host name
			if((pHTTPSession->HttpFlags & HTTPCLIENT_FLAG_USINGPROXY) != HTTPCLIENT_FLAG_USINGPROXY)
			{
				// No proxy, directly resolving the host name
				// Prep the parameter
				if((pHTTPSession->HttpFlags & HTTPCLIENT_FLAG_URLANDPORT) == HTTPCLIENT_FLAG_URLANDPORT)
				{
					nNullOffset = pHTTPSession->HttpUrl.UrlHost.nLength - pHTTPSession->HttpUrl.UrlPort.nLength - 1;
				}
				else
				{
					nNullOffset = pHTTPSession->HttpUrl.UrlHost.nLength;
				}

				Backup = HTTPStrExtract(pHTTPSession->HttpUrl.UrlHost.pParam,nNullOffset,0);
			}
			/*printf("[%s(%d)] UrlBsee %s\n", __FUNCTION__, __LINE__, pHTTPSession->HttpUrl.UrlBsee.pParam);
			printf("[%s(%d)] UrlHost %s\n", __FUNCTION__, __LINE__, pHTTPSession->HttpUrl.UrlHost.pParam);
			printf("[%s(%d)] UrlPort %s\n", __FUNCTION__, __LINE__, pHTTPSession->HttpUrl.UrlPort.pParam);
			printf("[%s(%d)] UrlRequest %s\n", __FUNCTION__, __LINE__, pHTTPSession->HttpUrl.UrlRequest.pParam);
			printf("[%s(%d)] Url %s\n", __FUNCTION__, __LINE__, pHTTPSession->HttpUrl.Url);*/

			ServerAddress.sin_port		= pHTTPSession->HttpUrl.nPort;	// Proxy Port number
			nRetCode = HTTPWrapperSSLConnect(&pHTTPSession->HttpConnection.TlsDataParams,	// Socket
				(HTTP_SOCKADDR_IN*)&ServerAddress, 		// Server address
				sizeof(HTTP_SOCKADDR_IN),					// Length of server address structure
				pHTTPSession->HttpUrl.UrlHost.pParam); 							// Hostname (ToDo: Fix this)

			// Restore from backup (fix the buffer)
			HTTPStrExtract(pHTTPSession->HttpUrl.UrlHost.pParam,nNullOffset,Backup);
			break;
		}
		// Zero the socket events
		FD_ZERO(&pHTTPSession->HttpConnection.FDRead);
		FD_ZERO(&pHTTPSession->HttpConnection.FDWrite);
		FD_ZERO(&pHTTPSession->HttpConnection.FDError);

		if(pHTTPSession->HttpConnection.HttpSocket == HTTP_INVALID_SOCKET)
		{
			// Create a TCP/IP stream socket
			pHTTPSession->HttpConnection.HttpSocket = socket(AF_INET,	    // Address family
			    SOCK_STREAM,			                    // Socket type
			    IPPROTO_TCP);		                        // Protocol
		}

		// Exit if we don't have a valid socket
		if(pHTTPSession->HttpConnection.HttpSocket == HTTP_INVALID_SOCKET)
		{
			nRetCode = HTTPCLIENT_ERROR_SOCKET_INVALID;
			break;
		}
		// Set non blocking socket
#ifdef _WIN32
		nRetCode = ioctlsocket(pHTTPSession->HttpConnection.HttpSocket, FIONBIO, &nNonBlocking);
#else
		nRetCode = sktSetNonblocking(pHTTPSession->HttpConnection.HttpSocket , nNonBlocking);
#endif
		if(nRetCode != 0)
		{
			nRetCode = HTTPCLIENT_ERROR_SOCKET_CANT_SET;
			break;
		}
		// Resolve the target host name
		if((pHTTPSession->HttpFlags & HTTPCLIENT_FLAG_USINGPROXY) != HTTPCLIENT_FLAG_USINGPROXY)
		{
			// No proxy, directly resolving the host name
			// Prep the parameter
			if((pHTTPSession->HttpFlags & HTTPCLIENT_FLAG_URLANDPORT) == HTTPCLIENT_FLAG_URLANDPORT)
			{
				nNullOffset = pHTTPSession->HttpUrl.UrlHost.nLength - pHTTPSession->HttpUrl.UrlPort.nLength - 1;
			}
			else
			{
				nNullOffset = pHTTPSession->HttpUrl.UrlHost.nLength;
			}

			Backup = HTTPStrExtract(pHTTPSession->HttpUrl.UrlHost.pParam,nNullOffset,0);
			// Resolve the host name
			nRetCode = (INT32)HostByName(pHTTPSession->HttpUrl.UrlHost.pParam,(unsigned long*)&Address);

			// Restore from backup (fix the buffer)
			HTTPStrExtract(pHTTPSession->HttpUrl.UrlHost.pParam,nNullOffset,Backup);
		}
		else
		{
			// Using a Proxy server so resolve the proxy host name
			nRetCode = (INT32)HostByName(pHTTPSession->HttpProxy.ProxyHost,(unsigned long*)&Address);
		}

		// See if we have a valid response from the net resolve operation
		/*
		if(nRetCode)
		{
			nRetCode = HTTPCLIENT_ERROR_SOCKET_RESOLVE;
			break;
		}
		*/
		// Reset the address structures
		memset(&ServerAddress, 0, sizeof(HTTP_SOCKADDR_IN));
		memset(&LoaclAddress, 0, sizeof(HTTP_SOCKADDR_IN));

		// Fill in the address structure
		ServerAddress.sin_family        = AF_INET;
		ServerAddress.sin_addr.s_addr   = Address;       // Server's address
		if((pHTTPSession->HttpFlags & HTTPCLIENT_FLAG_USINGPROXY) != HTTPCLIENT_FLAG_USINGPROXY)
		{
			// Use the remote web server port
			ServerAddress.sin_port      = htons(pHTTPSession->HttpUrl.nPort);        // Host Port number
		}
		else
		{
			// Use the proxy port
			ServerAddress.sin_port      = htons(pHTTPSession->HttpProxy.nProxyPort);  // Proxy Port number
		}

		// Client-side Binding
		if(pHTTPSession->HttpConnection.HttpClientPort != 0)
		{
			LoaclAddress.sin_family         = AF_INET;
			LoaclAddress.sin_port           = htons((unsigned short)pHTTPSession->HttpConnection.HttpClientPort);

			nRetCode = bind(pHTTPSession->HttpConnection.HttpSocket,
			    (HTTP_SOCKADDR*)&LoaclAddress,
			    sizeof(HTTP_SOCKADDR_IN));

			if(nRetCode != 0)
			{
				nRetCode = HTTPCLIENT_ERROR_SOCKET_BIND;
			}
		}

		// Connect using TLS or otherwise clear connection
		if((pHTTPSession->HttpFlags & HTTPCLIENT_FLAG_SECURE) == HTTPCLIENT_FLAG_SECURE)
		{ // Is it a TLS connection?
			//nRetCode = HTTPWrapperSSLConnect(pHTTPSession->HttpConnection.HttpSocket,	// Socket
			//    (HTTP_SOCKADDR*)&ServerAddress,	        // Server address
			//    sizeof(HTTP_SOCKADDR),                  // Length of server address structure
			//    "desktop");	                            // Hostname (ToDo: Fix this)
			/* [DX]For tls */
		nRetCode = HTTPWrapperSSLConnect(&pHTTPSession->HttpConnection.TlsDataParams,	// Socket
			(HTTP_SOCKADDR_IN*)&ServerAddress, 		// Server address
			sizeof(HTTP_SOCKADDR),					// Length of server address structure
			pHTTPSession->HttpUrl.UrlHost.pParam); 							// Hostname (ToDo: Fix this)
		}
		else    // Non TLS so..
		{
			nRetCode = connect(pHTTPSession->HttpConnection.HttpSocket,	// Socket
			    (HTTP_SOCKADDR*)&ServerAddress,			                // Server address
			    sizeof(HTTP_SOCKADDR));		                    // Length of server address structure
		}

		// The socket was set to be asyn so we should check the error being returned from connect()
#ifdef _WIN32
		nRetCode = SocketGetErr(pHTTPSession->HttpConnection.HttpSocket);
#else
		if (nRetCode == -1) {
		#ifdef _SPCA6350
			if (!errno) {
				nRetCode = soc_geterr(pHTTPSession->HttpConnection.HttpSocket);
			}
			else {
				nRetCode = errno;
			}
		#else
			nRetCode = errno;
		#endif
		}
#endif
		if(nRetCode == 0 || nRetCode == HTTP_EWOULDBLOCK || nRetCode == HTTP_EINPROGRESS)
		{
			//INT32			nSocketEvents;					 // Socket events center
			//HTTP_TIMEVAL	Timeval 			= { 1 , 0 };  // Timeout value for the socket() method
			// Set TLS Nego flag to flase
			pHTTPSession->HttpConnection.TlsNego = FALSE;
			// Set the Write fd_sets for a socket connection event
			FD_SET(pHTTPSession->HttpConnection.HttpSocket, &pHTTPSession->HttpConnection.FDWrite);
			#if 0  //DXTEST
			do {
	            // See if we got any events on the socket
	            nSocketEvents = select((pHTTPSession->HttpConnection.HttpSocket + 1), 0,
	                &pHTTPSession->HttpConnection.FDWrite,
	                0,
	                &Timeval);

	            if(nSocketEvents > 0) // New events on the socket
	            {
	                break;
	            }
			} while(1);
            // Socket is writable (check connected)
            if(FD_ISSET(pHTTPSession->HttpConnection.HttpSocket, &pHTTPSession->HttpConnection.FDWrite))
            {
				socklen_t lon;
				int valopt;
				lon = sizeof(int);
				if ( getsockopt(pHTTPSession->HttpConnection.HttpSocket, SOL_SOCKET, SO_ERROR, (void*)(&valopt), &lon) == -1 ) {
					if (valopt) {
						//HOST_PROF_LOG_PRINT(LEVEL_ERROR, "[%s(%d)] connect error valopt %d",__FUNCTION__,__LINE__,valopt);
						printf("[%s(%d)] connect error valopt %d\n",__FUNCTION__,__LINE__,valopt);
						// Socket connection problem
						nRetCode = HTTPCLIENT_ERROR_SOCKET_CONNECT;
						break;
					}
				}
            }
			#endif
			// Set the state flag
			pHTTPSession->HttpState  = pHTTPSession->HttpState | HTTPCLIENT_STATE_HOST_CONNECTED;
			// We have connected so set the return value to success
			nRetCode = HTTPCLIENT_SUCCESS;
			break;
		}
		else
		{
			// Socket connection problem
			nRetCode = HTTPCLIENT_ERROR_SOCKET_CONNECT;
			break;
		}
	}while(0);
#if defined(_HTTP_DEBUGGING_)
	if(pHTTPSession->pDebug)
	{
		pHTTPSession->pDebug("HTTPIntrnConnectionOpen",NULL,0,"");
	}
#endif

	return nRetCode;
}

#if defined(_SPCA6350)
#include "sp5k_global_api.h"

static void app_http_usleep(
	UINT32 us
)
{
    UINT32 ms = (us / 1000);
    if (us > 0) {
        if (ms >= 10) {
            #ifdef SPCA6350
            sp5kOsThreadSleep((UINT32)ms);
            #else
            ros_thread_sleep((UINT32)ms);
            #endif
        } else {
            sp5kTimeDelay(SP5K_TIME_DELAY_1US, us);
        }
    }
}
#endif

///////////////////////////////////////////////////////////////////////////////
//
// Function     : HTTPIntrnSend
// Purpose      : Send data to the remote server (Asynchronous sockets)
// Returns      : HTTP Status
// Last updated : 01/09/2005
//
///////////////////////////////////////////////////////////////////////////////

static UINT32 HTTPIntrnSend (P_HTTP_SESSION pHTTPSession,
                      CHAR *pData,            // [IN] a pointer to the data to be sent
                      UINT32 *nLength)        // [IN OUT] Length of data to send and the transmitted bytes count
{

    INT32           nSocketEvents;                   // Socket events center
    INT32           nRetCode            = HTTPCLIENT_SUCCESS;  // a function return code value
    HTTP_TIMEVAL    Timeval             = { 1 , 0 };  // Timeout value for the socket() method
    httpclientConnection_t *pConnection        = NULL;      // Pointer for the connection structure


    do
    {
        // Validate the session pointer
        if(!pHTTPSession)
        {
            nRetCode = HTTPCLIENT_ERROR_INVALID_HANDLE;
            break;
        }

        // Have a pointer on the internal connection structure for simplifying code reading
        pConnection = &pHTTPSession->HttpConnection;

        while(1)
        {

            // Check for timeout
            //if(HTTPIntrnSessionEvalTimeout(pHTTPSession) == TRUE)
            //{
            //    nRetCode = HTTPCLIENT_ERROR_SOCKET_TIME_OUT;
            //    break;
            //}
			/* [DX]For ssl tls Send the data */
			if((pHTTPSession->HttpFlags & HTTPCLIENT_FLAG_SECURE) == HTTPCLIENT_FLAG_SECURE)
			{
				// TLS Protected connection
				if(pConnection->TlsNego == FALSE)
				{
					nRetCode = HTTPWrapperSSLNegotiate(pConnection->HttpSocket,0,0,"desktop");
					if(nRetCode != 0)
					{
						// TLS Error
						nRetCode = HTTPCLIENT_ERROR_TLS_NEGO;
						break;
					}
					pConnection->TlsNego = TRUE;
				}
				nRetCode = HTTPWrapperSSLSend(pHTTPSession->HttpConnection.TlsDataParams, (unsigned char *)pData,*(nLength),0);
                if(nRetCode == SOCKET_ERROR)
                {
                    nRetCode = HTTPCLIENT_ERROR_SOCKET_SEND;
                    break;
                }
                // The data was sent to the remote server
                *(nLength) = nRetCode;
                nRetCode = HTTPCLIENT_SUCCESS;
				break;
			}

            // Reset socket events , only Error, since we don't want to get
            // a repeated Write events (socket is connected)

            FD_SET(pConnection->HttpSocket, &pConnection->FDError);

            // See if we got any events on the socket
            nSocketEvents = select((pConnection->HttpSocket + 1), 0,
                &pConnection->FDWrite,
                &pConnection->FDError,
                &Timeval);

            if(nSocketEvents < 0) // No events on the socket
            {
                *(nLength) = 0;
                break; // To-Do: This might be an error
            }

            if(nSocketEvents == 0) // No new events so
            {
				// Check for timeout
				if(HTTPIntrnSessionEvalTimeout(pHTTPSession) == TRUE)
				{
					nRetCode = HTTPCLIENT_ERROR_SOCKET_TIME_OUT;
					break;
				}
				#if 0//defined(_SPCA6350)
				app_http_usleep(200);
				#endif
                continue; // restart this loop
            }
			//printf("%d-", *nLength);
            // We had a socket related error
            if(FD_ISSET(pConnection->HttpSocket ,&pConnection->FDError))
            {
                FD_CLR((UINT32)pConnection->HttpSocket,&pConnection->FDError);
                *(nLength) = 0;

				printf("[ERROR]To-Do: Handle this case[%d].\n", nRetCode);
				printf("[ERROR]SocketGetErr[%d].\n", SocketGetErr(pHTTPSession->HttpConnection.HttpSocket));
                // To-Do: Handle this case
                nRetCode = HTTPCLIENT_ERROR_SOCKET_SEND;
                break;
            }

            // Socket is writable (we are connected) so send the data
            if(FD_ISSET(pConnection->HttpSocket ,&pConnection->FDWrite))
            {

                // Send the data
                if((pHTTPSession->HttpFlags & HTTPCLIENT_FLAG_SECURE) == HTTPCLIENT_FLAG_SECURE)
                {
                    // TLS Protected connection
                    if(pConnection->TlsNego == FALSE)
                    {
                        nRetCode = HTTPWrapperSSLNegotiate(pConnection->HttpSocket,0,0,"desktop");
                        if(nRetCode != 0)
                        {
                            // TLS Error
                            nRetCode = HTTPCLIENT_ERROR_TLS_NEGO;
                            break;
                        }
                        pConnection->TlsNego = TRUE;
                    }
                    nRetCode = HTTPWrapperSSLSend(pConnection->HttpSocket, (unsigned char *)pData,*(nLength),0);
                }
                else
                {
                    nRetCode = send(pConnection->HttpSocket,pData,*(nLength),0);
                }
                if(nRetCode == SOCKET_ERROR)
                {
                    nRetCode = SocketGetErr(pHTTPSession->HttpConnection.HttpSocket);
					if(nRetCode != 11) {
						printf("[ERROR]SocketGetErr errno[%d].\n", nRetCode);
	                    nRetCode = HTTPCLIENT_ERROR_SOCKET_SEND;
	                    break;
					}
					nRetCode = 0;
                }
                // The data was sent to the remote server
                *(nLength) = nRetCode;
                nRetCode = HTTPCLIENT_SUCCESS;
                break;
            }

        }
    } while(0);

#if defined(_HTTP_DEBUGGING_)
    if(pHTTPSession->pDebug)
    {
        pHTTPSession->pDebug("HTTPIntrnSend",pData,*(nLength),"");
    }
#endif


    return nRetCode;
}

///////////////////////////////////////////////////////////////////////////////
//
// Function     : HTTPIntrnRecv
// Purpose      : Receive data from the connected socket using asynchronous sockets
// Returns      : HTTP Status
// Last updated : 01/09/2005
//
///////////////////////////////////////////////////////////////////////////////

static UINT32 HTTPIntrnRecv (P_HTTP_SESSION pHTTPSession,
                      CHAR *pData,        // [IN] a pointer for a buffer that receives the data
                      UINT32 *nLength,    // [IN OUT] Length of the buffer and the count of the received bytes
                      BOOL PeekOnly)      // [IN] State if we should only peek the socket (default is no)
{
	INT32           nSocketEvents;
	INT32           nRetCode = HTTPCLIENT_SUCCESS;
	HTTP_TIMEVAL    Timeval         = { 0, 50000 };
	httpclientConnection_t *pConnection     = NULL;

    do
	{
		if(!pHTTPSession)
		{
			nRetCode = HTTPCLIENT_ERROR_INVALID_HANDLE;
			break;
		}

		// Set a pointer on the session internal connection structure (simplify code reading)
		pConnection = &pHTTPSession->HttpConnection;
		while(1)
		{
			// Check for timeout
			//if(HTTPIntrnSessionEvalTimeout(pHTTPSession) == TRUE)
			//{
			//	nRetCode =  HTTPCLIENT_ERROR_SOCKET_TIME_OUT;
			//	break;
			//}
			/* [DX]For ssl tls */
			if((pHTTPSession->HttpFlags & HTTPCLIENT_FLAG_SECURE) == HTTPCLIENT_FLAG_SECURE)
			{
				nRetCode =	HTTPWrapperSSLRecvPending(pHTTPSession->HttpConnection.TlsDataParams);
				if(nRetCode > 0)
				{
					// Recive without being notified by the socket event
					if((nRetCode = HTTPWrapperSSLRecv(pHTTPSession->HttpConnection.TlsDataParams, (unsigned char *)pData,*(nLength),0)) == SOCKET_ERROR)
					{
						// Socket error
						nRetCode =	HTTPCLIENT_ERROR_SOCKET_RECV;
						break;
					}
					*(nLength) = nRetCode;
					// Break on no data or server connection reset
		#ifdef _WIN32
					if ( nRetCode == 0 || nRetCode == HTTP_ECONNRESET)
		#else
					if ( nRetCode == 0 /*|| nRetCode == HTTP_ECONNRESET*/)
		#endif
					{
						// Connection closed, simply break - this is not an error
						nRetCode =	HTTPCLIENT_EOS;  // Signal end of stream
						break;
					}
					// We have successfully got the data from the server
					nRetCode = HTTPCLIENT_SUCCESS;
					break;
				}
			}

			// Reset socket events
			#if 0 /*[DX]20181017 Add for Socket status reset. */
			FD_ZERO(&pConnection->FDRead);
			FD_ZERO(&pConnection->FDError);
			#endif
			FD_SET(pConnection->HttpSocket, &pConnection->FDRead);
			FD_SET(pConnection->HttpSocket, &pConnection->FDError);

			// See if we got any events on the socket
			nSocketEvents = select(pConnection->HttpSocket + 1, &pConnection->FDRead,
			    0,
			    &pConnection->FDError,
			    &Timeval);

			if(nSocketEvents < 0) // Error or no new socket events
			{
				*(nLength) = 0;
				break;
			}
			if(nSocketEvents == 0)
			{
				///////////////////////////////////////////////////////////////////////////////////////////////////////////
				// This is a simple bypass for the TSL session (for some reason the socket read event is not set so
				// The pending bytes on the socket are being checked manualy.
				// TLS hack:

				if((pHTTPSession->HttpFlags & HTTPCLIENT_FLAG_SECURE) == HTTPCLIENT_FLAG_SECURE)
				{
					nRetCode = 	HTTPWrapperSSLRecvPending(pHTTPSession->HttpConnection.TlsDataParams);
					if(nRetCode > 0)
					{
						// Recive without being notified by the socket event
						if((nRetCode = HTTPWrapperSSLRecv(pHTTPSession->HttpConnection.TlsDataParams, (unsigned char *)pData, *(nLength),0)) == SOCKET_ERROR)
						{
							// Socket error
							nRetCode =  HTTPCLIENT_ERROR_SOCKET_RECV;
							break;
						}
						*(nLength) = nRetCode;
						// Break on no data or server connection reset
						#ifdef _WIN32
						if ( nRetCode == 0 || nRetCode == HTTP_ECONNRESET)
						#else
						if ( nRetCode == 0 /*|| nRetCode == HTTP_ECONNRESET*/)
						#endif
						{
							// Connection closed, simply break - this is not an error
							nRetCode =  HTTPCLIENT_EOS;  // Signal end of stream
							break;
						}
						// We have successfully got the data from the server
						nRetCode = HTTPCLIENT_SUCCESS;
						break;
					}
				}
				// End Of the TLS bypass section
				//
				/////////////////////////////////////////////////////////////////////////////////////////////////
				// Check for timeout
				if(HTTPIntrnSessionEvalTimeout(pHTTPSession) == TRUE)
				{
					nRetCode =	HTTPCLIENT_ERROR_SOCKET_TIME_OUT;
					break;
				}
				#if 1  //template bug fix
				#if defined(_SPCA6350)
				app_http_usleep(200);
				#endif
				#endif
				continue; // select() timed out - restart this loop
			}
			if(FD_ISSET(pConnection->HttpSocket ,&pConnection->FDRead)) // Are there any read events on the socket ?
			{
				// Clear the event
				FD_CLR((UINT32)pConnection->HttpSocket,&pConnection->FDRead);

				// Socket is readable so so read the data
				if(PeekOnly == FALSE)
				{
					// Get the data (secuure)
					if((pHTTPSession->HttpFlags & HTTPCLIENT_FLAG_SECURE) == HTTPCLIENT_FLAG_SECURE)
					{
						if((nRetCode = HTTPWrapperSSLRecv(pConnection->HttpSocket, (unsigned char *)pData,*(nLength),0)) == SOCKET_ERROR)
						{
							// Socket error
							nRetCode =  HTTPCLIENT_ERROR_SOCKET_RECV;
							break;
						}
					}
					else  // Get the data (non secuure)
					{
						if((nRetCode = recv(pConnection->HttpSocket,pData,*(nLength),0)) == SOCKET_ERROR)
						{
                            // Socket error
                            nRetCode =  HTTPCLIENT_ERROR_SOCKET_RECV;
                            break;
                        }
						#if 0//SPCA6350 /* [DX]20181010 For test*/
						int try_time = 0;
						UINT32 _delay_us = 20*1000;
						while (nRetCode == 0 && try_time++ < 5) {
							printf("%s[%d]Socket recv try:%d, _delay:%dus\n", __func__, __LINE__, try_time, _delay_us*(try_time));

							app_http_usleep(_delay_us*(try_time));

							if((nRetCode = recv(pConnection->HttpSocket,pData,*(nLength),0)) == SOCKET_ERROR)
	                        {
	                            // Socket error
								printf("%s[%d]Socket error:%d\n", __func__, __LINE__, nRetCode);
	                            break;
	                        }
						}
						if (nRetCode == SOCKET_ERROR) {
							nRetCode =	HTTPCLIENT_ERROR_SOCKET_RECV;
							break;
						}
						if (nRetCode == 0) {
							printf("%s[%d]Socket recv 0 bytes!!!\n", __func__, __LINE__);
						}
						//else {
						//	printf("R:%d\n", nRetCode);
						//}
						#endif
                    }

                }
				else
				{
					// Only peek te socket
					if((nRetCode = recv(pConnection->HttpSocket,pData,*(nLength),MSG_PEEK)) == SOCKET_ERROR)
					{
						// Socket error
						nRetCode =  HTTPCLIENT_ERROR_SOCKET_RECV;
						break;
					}
				}
				*(nLength) = nRetCode;
				// Break on no data or server connection reset
				// MSDN: If the connection has been gracefully closed, the return value is zero.
				#ifdef _WIN32
				if ( nRetCode == 0 || nRetCode == HTTP_ECONNRESET)
				#else
				if ( nRetCode == 0 /*|| nRetCode == HTTP_ECONNRESET*/)
				#endif
				{
					// Connection closed, simply break - this is not an error
					nRetCode =  HTTPCLIENT_EOS;  // Signal end of stream
					break;
				}
				// We have successfully got the data from the server
				nRetCode = HTTPCLIENT_SUCCESS;
				break;
			}

			// We had a socket related error
			if(FD_ISSET(pConnection->HttpSocket ,&pConnection->FDError))
			{
				FD_CLR((UINT32)pConnection->HttpSocket,&pConnection->FDError);
				*(nLength) = 0;

				// To-Do: Handle this case
				nRetCode = HTTPCLIENT_ERROR_SOCKET_RECV;
				break;
			}
		}
	}while(0);

	return nRetCode;
}


///////////////////////////////////////////////////////////////////////////////
//
// Function     : HTTPIntrnGetRemoteChunkLength
// Purpose      : Receive (byte by byte) the chunk parameter (while in chunk mode receive) and
//                Convert the HEX string into an integer
// Returns      : HTTP Status
// Last updated : 01/09/2005
//
///////////////////////////////////////////////////////////////////////////////

static UINT32 HTTPIntrnGetRemoteChunkLength (P_HTTP_SESSION pHTTPSession)
{

    UINT32          nBytesRead = 1;
    UINT32          nRetCode = HTTPCLIENT_SUCCESS;
    UINT32          nBytesCount = 0;
    CHAR            ChunkHeader[HTTPCLIENT_MAX_CHUNK_HEADER];
    CHAR            *pPtr;

    do
    {
        if(!pHTTPSession)
        {
            nRetCode = HTTPCLIENT_ERROR_INVALID_HANDLE;
            break;
        }

        // Read byte by byte until we get a CrLf
        pPtr = ChunkHeader; // Get a pointer to the received buffer
        *pPtr = 0;          // Terminate with null

        while(nBytesRead > 0)
        {
            // Receive a single byte
            nRetCode = HTTPIntrnRecv(pHTTPSession,pPtr,&nBytesRead,FALSE);
            // Did we succeed?
            if(nRetCode == HTTPCLIENT_SUCCESS && nBytesRead > 0)
            {
                // Increment the bytes count
                nBytesCount += nBytesRead;
                if(nBytesRead > HTTPCLIENT_MAX_CHUNK_HEADER)
                {
                    // Error chunk buffer is full
                    nRetCode = HTTPCLIENT_ERROR_CHUNK_TOO_BIG;
                    break;
                }
                // Don't Process if the fist 2 bytes are CrLf.
                if(! ((nBytesCount == 1 && *pPtr == 0x0d) || (nBytesCount == 2 && *pPtr == 0x0a)))
                {
                    // Advance the pointer by the received data length
                    pPtr += nBytesRead;
                    // Look for CrLf in the last 2 bytes
                    if(memcmp(pPtr - 2,HTTPCLIENT_CRLF,2) == 0)
                    {
                        // Chunk Header was received
                        *pPtr = 0;  // null terminate the chunk parameter
                        pHTTPSession->HttpCounters.nRecivedChunkLength = HTTPStrHToL(ChunkHeader); // Convert to a number
                        // Set the HTTP counters
                        pHTTPSession->HttpCounters.nBytesToNextChunk =  pHTTPSession->HttpCounters.nRecivedChunkLength;
                        break;
                    }
                }
            }
            else // Socket Error
            {
                nRetCode = 0;
                break;
            }
        }
    } while(0);

#if defined(_HTTP_DEBUGGING_)
    if(pHTTPSession->pDebug)
    {
        pHTTPSession->pDebug("HTTPIntrnGetRemoteChunkLength",NULL,0,"Next chunk is %d bytes",pHTTPSession->HttpCounters.nRecivedChunkLength);
    }
#endif

    return nRetCode;
}

///////////////////////////////////////////////////////////////////////////////
//
// Function     : HTTPIntrnGetRemoteHeaders
// Purpose      : Perform a socket receive (byte by byte) until all the HTTP headers are received
// Returns      : HTTP Status
// Last updated : 01/09/2005
//
///////////////////////////////////////////////////////////////////////////////

static UINT32 HTTPIntrnGetRemoteHeaders (P_HTTP_SESSION pHTTPSession)
{

    UINT32          nBytesRead = 1;
    UINT32          nRetCode = HTTPCLIENT_SUCCESS;
    UINT32          nProjectedHeaderLength;
    UINT32          nProjectedBufferLength;
    INT32           nCurrentfreeSpace;
    INT32           nProjectedfreeSpace;
    CHAR            *pPtr;

    do
    {
        if(!pHTTPSession)
        {
            nRetCode = HTTPCLIENT_ERROR_INVALID_HANDLE;
            break;
        }

        // Read byte by byte until we get CrLf followed by CrLf
        // Set the incoming headers pointer

        if(!pHTTPSession->HttpHeaders.HeadersIn.pParam)
        {
            /// The incoming headers starts where the outgoing headers ends
            pHTTPSession->HttpHeaders.HeadersIn.pParam = pHTTPSession->HttpHeaders.HeadersOut.pParam + pHTTPSession->HttpHeaders.HeadersOut.nLength;
        }

        // Receive until we get all the headers or any other error event
        while(nBytesRead > 0)
        {

            // Size of the projected buffer we are going to receive
            nProjectedHeaderLength  = nBytesRead;
            // Size of the projected total incoming buffer
            nProjectedBufferLength  = nProjectedHeaderLength + pHTTPSession->HttpHeaders.HeadersOut.nLength + pHTTPSession->HttpHeaders.HeadersIn.nLength;
            // Current free space on the incoming headers buffer
            nCurrentfreeSpace       = pHTTPSession->HttpHeaders.HeadersBuffer.nLength - (pHTTPSession->HttpHeaders.HeadersOut.nLength + pHTTPSession->HttpHeaders.HeadersIn.nLength);
            // Projected free space after the completion of the receive
            nProjectedfreeSpace     = nCurrentfreeSpace - nProjectedHeaderLength;

            // Check total size limit
            if(nProjectedBufferLength > HTTPCLIENT_MAX_SEND_RECV_HEADERS)
            {
                printf("[ERROR]nProjectedBufferLength:need %ld but %d\n", (long int)nProjectedBufferLength, HTTPCLIENT_MAX_SEND_RECV_HEADERS);
                return HTTPCLIENT_ERROR_NO_MEMORY;
            }

            if((INT32)nProjectedfreeSpace < 0)
            {
                if(HTTPCLIENT_MEMORY_RESIZABLE == FALSE)
                {
                    // Need more space but we can't grow beyond the current size
                    nRetCode = HTTPCLIENT_ERROR_NO_MEMORY;
                    break;
                }
                else
                {
                    // We can resizes so..
                    nRetCode = HTTPIntrnResizeBuffer(pHTTPSession,nProjectedBufferLength + HTTPCLIENT_MEMORY_RESIZE_FACTOR);
                    if(nRetCode != HTTPCLIENT_SUCCESS)
                    {
                        break;
                    }
                }
            }
            // Jump to the beginning of the incoming headers (just after the end of the outgoing headers)
            pPtr = pHTTPSession->HttpHeaders.HeadersIn.pParam + pHTTPSession->HttpHeaders.HeadersIn.nLength;
            // Read a single byte
            nRetCode = HTTPIntrnRecv(pHTTPSession,pPtr,&nBytesRead,FALSE);

            // ToDo: Break if not getting HTTP on the first 4 bytes

            if(nRetCode == HTTPCLIENT_SUCCESS && nBytesRead > 0)
            {
                // Advance the pointer by 1 byte
                pPtr += nBytesRead;
                // Increase the total receive length
                pHTTPSession->HttpHeaders.HeadersIn.nLength++;

                // Set the HTTP counters
                pHTTPSession->HttpCounters.nRecivedHeaderLength++;

                if(memcmp(pPtr - 4,HTTPCLIENT_CRLFX2,4) == 0)
                {
                    // Headers were received
                    break;
                }
            }
            else
            {
                nRetCode =  HTTPCLIENT_ERROR_HEADER_RECV; // This was marked out for some reason
                break;
            }
        }
    }while(0);


#if defined(_HTTP_DEBUGGING_)
    if(pHTTPSession->pDebug)
    {
        pHTTPSession->pDebug("HTTPIntrnGetRemoteHeaders",NULL,0,"Got %d bytes",pHTTPSession->HttpHeaders.HeadersIn.nLength);
    }
#endif

    return nRetCode;
}

///////////////////////////////////////////////////////////////////////////////
//
// Function     : HTTPIntrnHeadersFind
// Purpose      : Look for a header (insensitive search) by its name
// Gets         : HTTP Status
// Last updated : 01/09/2005
//
///////////////////////////////////////////////////////////////////////////////

static UINT32 HTTPIntrnHeadersFind (P_HTTP_SESSION pHTTPSession,CHAR *pHeaderName,
                             httpclientParam_t *pParam,     //  [OUT] HTTP parameter structure that holds the search results
                             BOOL IncommingHeaders,  //  [IN]  Indicate if we are to search in the incoming or outgoing headers
                             UINT32 nOffset)         //  [IN]  Optionaly privide an offset to start looking from
{
    CHAR           *pHeaderEnd;
    CHAR           Header[HTTPCLIENT_MAX_HEADER_SEARCH_CLUE]; // To-Do: Use pointers insted of fixed length
    UINT32         nLength;
    UINT32         nRetCode = HTTPCLIENT_ERROR_HEADER_NOT_FOUND;

    do
    {
        if(!pHTTPSession)
        {
            nRetCode = HTTPCLIENT_ERROR_INVALID_HANDLE;
            break;
        }

        // Reset the input parameter structure
        pParam->pParam  = NULL;
        pParam->nLength = 0;
        // Get the requested header length
        nLength = strlen(pHeaderName);
        if(nLength > (HTTPCLIENT_MAX_HEADER_SEARCH_CLUE - 3))
        {
            // Error : header search clue too big
            nRetCode = HTTPCLIENT_ERROR_HEADER_BIG_CLUE;
            break;
        }
        // Build the searched header name , add a leading CrLf before the header name and trailing ":"
        memset(Header,0x00,HTTPCLIENT_MAX_HEADER_SEARCH_CLUE);
        strcpy(Header,HTTPCLIENT_CRLF);
        strcat(Header,pHeaderName);
        strcat(Header,":");
        // Case insensitive search for the header name (search the incoming headers)
        if(IncommingHeaders == TRUE)
        {
            pParam->pParam = HTTPStrCaseStr(pHTTPSession->HttpHeaders.HeadersIn.pParam + nOffset,
                pHTTPSession->HttpHeaders.HeadersIn.nLength,
                Header);
        }
        else
        {
            // Optionally search the outgoing headers
            pParam->pParam = HTTPStrCaseStr(pHTTPSession->HttpHeaders.HeadersOut.pParam + nOffset,
                pHTTPSession->HttpHeaders.HeadersOut.nLength,
                Header);
        }

        if(pParam->pParam) // Did we find it?
        {
            // Search for the token end (trailing CrLf)
            pHeaderEnd = strstr(pParam->pParam + 2,HTTPCLIENT_CRLF);
            if(pHeaderEnd)
            {
                // Get the length (up to the CrLf)
                pParam->nLength =  pHeaderEnd -  pParam->pParam;
                nRetCode = HTTPCLIENT_SUCCESS;
                break;

            }
        }
    }while(0);

    // Could not find the header
    return nRetCode;
}

///////////////////////////////////////////////////////////////////////////////
//
// Function     : HTTPIntrnAuthenticate
// Purpose      :
// Returns      : HTTP Status
// Last updated : 01/09/2005
//
///////////////////////////////////////////////////////////////////////////////

static UINT32 HTTPIntrnAuthenticate(P_HTTP_SESSION pHTTPSession)
{
    UINT32      nRetCode = HTTPCLIENT_SUCCESS;   // Function call return code
    UINT32      nBytes = 32;
    UINT32      nTotalBytes = 0;
    CHAR        ErrorPage[32];
    BOOL        NewConnection = FALSE;


    do
    {
        // Validate the session pointer
        if(!pHTTPSession)
        {
            nRetCode = HTTPCLIENT_ERROR_INVALID_HANDLE;
            break;
        }

        //  Handle connection close message (reconnect)
        if(pHTTPSession->HttpHeadersInfo.Connection == FALSE)
        {
            // Gracefully close the connection and set the socket as invalid
            if(pHTTPSession->HttpConnection.HttpSocket != HTTP_INVALID_SOCKET)
            {
                HTTPIntrnConnectionClose(pHTTPSession);
            }
            // Connect to the remote server (or proxy)
            nRetCode = HTTPIntrnConnectionOpen(pHTTPSession);
            if(nRetCode != HTTPCLIENT_SUCCESS)
            {
                break;
            }

            NewConnection = TRUE;
        }

        // Analyze the security headers and optionally build the authentication reply header
        if((nRetCode = HTTPIntrnParseAuthHeader(pHTTPSession))!= HTTPCLIENT_SUCCESS)
        {
            break;
        }
        // We have to recive any HTML data here inorder to "Clear" the socket buffer for later usage
        // Note: We should skip this when the HEAD verb was used
        while(NewConnection == FALSE && pHTTPSession->HttpHeaders.HttpLastVerb != HTTPCLIENT_HEAD && pHTTPSession->HttpHeadersInfo.nHTTPContentLength > 0 && nBytes > 0)
        {
            ErrorPage[0] = 0;
            if((nRetCode = HTTPIntrnRecv(pHTTPSession,ErrorPage,&nBytes,FALSE)) != HTTPCLIENT_SUCCESS)
            {
                break;
            }

            nTotalBytes += nBytes;
            if(nTotalBytes >= pHTTPSession->HttpHeadersInfo.nHTTPContentLength)
            {
                break;
            }
        }

        // Re-Send the headers after having analyzed the authorizaton headers
        if((nRetCode = HTTPIntrnHeadersSend(pHTTPSession,pHTTPSession->HttpHeaders.HttpVerb)) != HTTPCLIENT_SUCCESS)
        {
            break;
        }

    }while(0);

#if defined(_HTTP_DEBUGGING_)
    if(pHTTPSession->pDebug)
    {
        pHTTPSession->pDebug("HTTPIntrnAuthenticate",NULL,0,"");
    }
#endif

    return nRetCode;

}

///////////////////////////////////////////////////////////////////////////////
//
// Function     : HTTPIntrnHeadersParse
// Purpose      : Parse the HTTP incoming headers.
// Returns      : HTTP Status
// Last updated : 01/09/2005
//
///////////////////////////////////////////////////////////////////////////////

static UINT32 HTTPIntrnHeadersParse (P_HTTP_SESSION pHTTPSession)
{

    CHAR            *pPtr;                                      // a pointer that points on the incoming headers
    UINT32          nTokenLength = 0;                           // Length of the parsed token
    UINT32          nRetCode = HTTPCLIENT_SUCCESS;             // a function return code value
    UINT32          nOffset = 0;                                // Bytes offset (strings comperision)
    CHAR            HTTPToken[HTTPCLIENT_MAX_TOKEN_LENGTH];    // Buffer for the parsed HTTP token
    httpclientParam_t      HTTPParam;                                  // A generic pointer\length parameter for parsing
    BOOL            AuthHeaders = FALSE;                        // While we are searching the authentication methods

    do
    {
        // Validate the session pointer
        if(!pHTTPSession)
        {
            nRetCode = HTTPCLIENT_ERROR_INVALID_HANDLE;
            break;
        }

#if defined(_HTTP_DEBUGGING_)
        if(pHTTPSession->pDebug)
        {
            pHTTPSession->pDebug("HTTPIntrnHeadersParse",pHTTPSession->HttpHeaders.HeadersIn.pParam,pHTTPSession->HttpHeaders.HeadersIn.nLength,"[Incomming Headers]");
        }
#endif


        // Set a pointer on the incoming headers
        pPtr = pHTTPSession->HttpHeaders.HeadersIn.pParam;

        // Detect the leading HTTP string
        if(HTTPStrInsensitiveCompare(pPtr,"http",4) != TRUE)
        {
            nRetCode = HTTPCLIENT_ERROR_BAD_HEADER;
            break;
        }

        // Get the HTTP Version
        while ((*pPtr) && (*pPtr != 0x20))
        {
            nTokenLength++;
            pPtr++; // Move to the first space
        }
        strncpy(pHTTPSession->HttpHeadersInfo.HTTPVersion,
            pPtr - nTokenLength,
            MIN(15,nTokenLength));
        pPtr++;

        // Get the HTTP status code
        memset(HTTPToken,0x00,HTTPCLIENT_MAX_TOKEN_LENGTH);
        nTokenLength = 0;
        while ((*pPtr) && (*pPtr != 0x20))
        {
            nTokenLength++;
            pPtr++; // Move to the next space
        }
        strncpy(HTTPToken,(pPtr - nTokenLength),MIN(HTTPCLIENT_MAX_TOKEN_LENGTH,nTokenLength));

        pHTTPSession->HttpHeadersInfo.nHTTPStatus = atol(HTTPToken);

        // Search for content length
        pHTTPSession->HttpHeadersInfo.nHTTPContentLength = 0; // Default no unknown length
        // Look for the token
        if(HTTPIntrnHeadersFind(pHTTPSession,"content-length",&HTTPParam,TRUE,0) == HTTPCLIENT_SUCCESS)
        {

            memset(HTTPToken,0x00,HTTPCLIENT_MAX_TOKEN_LENGTH);        // Reset the token buffer
            nTokenLength  = HTTPCLIENT_MAX_TOKEN_LENGTH;               // Set the buffer length
            // Attempt to extract the token
            if(HTTPStrGetToken(HTTPParam.pParam,HTTPParam.nLength,HTTPToken,&nTokenLength))
            {
                // Convert the content-length into an integer.
                pHTTPSession->HttpHeadersInfo.nHTTPContentLength = atol(HTTPToken);
            }
        }

        // Search for connection status
        pHTTPSession->HttpHeadersInfo.Connection = TRUE; // Default status where no server connection header was detected
        // Look for token (can be standard connection or a proxy connection)
        if( (HTTPIntrnHeadersFind(pHTTPSession,"connection",&HTTPParam,TRUE,0) == HTTPCLIENT_SUCCESS) ||
            (HTTPIntrnHeadersFind(pHTTPSession,"proxy-connection",&HTTPParam,TRUE,0) == HTTPCLIENT_SUCCESS))
        {

            memset(HTTPToken,0x00,HTTPCLIENT_MAX_TOKEN_LENGTH);
            nTokenLength  = HTTPCLIENT_MAX_TOKEN_LENGTH;
            // Attempt to extract the token
            if(HTTPStrGetToken(HTTPParam.pParam,HTTPParam.nLength,HTTPToken,&nTokenLength))
            {
                // Is this a keep alive session?
                pHTTPSession->HttpHeadersInfo.Connection = HTTPStrInsensitiveCompare(HTTPToken,"keep-alive",0);
                // Is it a closed session
                if(HTTPStrInsensitiveCompare(HTTPToken,"close",0) == TRUE)
                {
                    pHTTPSession->HttpHeadersInfo.Connection = FALSE;
                }
            }
        }

        // Search for chunking mode transfer
        pHTTPSession->HttpFlags = pHTTPSession->HttpFlags &~ HTTPCLIENT_FLAG_CHUNKED; // Remove the flag
        if(HTTPIntrnHeadersFind(pHTTPSession,"transfer-encoding",&HTTPParam,TRUE,0) == HTTPCLIENT_SUCCESS)
        {

            memset(HTTPToken,0x00,HTTPCLIENT_MAX_TOKEN_LENGTH);
            nTokenLength  = HTTPCLIENT_MAX_TOKEN_LENGTH;
            if(HTTPStrGetToken(HTTPParam.pParam,HTTPParam.nLength,HTTPToken,&nTokenLength))
            {
                // If the chunks token was find then set the session flag accordingly
                if(HTTPStrInsensitiveCompare(HTTPToken,"chunked",0) == TRUE)
                {
                    pHTTPSession->HttpFlags  =  pHTTPSession->HttpFlags  | HTTPCLIENT_FLAG_CHUNKED;
                }
            }
        }
        // Look for the authentication header
		AuthHeaders = TRUE;  /* DX.JAING[20180828]For Oauth2 Authenticate, bypass. */
        while(AuthHeaders == FALSE)  // address multiple authentication methods presented by the server
        {
            if(pHTTPSession->HttpHeadersInfo.nHTTPStatus == HTTP_STATUS_UNAUTHORIZED)
            {
                // Double check for the "www-authenticate" header token
                if(HTTPIntrnHeadersFind(pHTTPSession,"www-authenticate",&pHTTPSession->HttpAuthHeader.AuthHeader,TRUE,nOffset) != HTTPCLIENT_SUCCESS)
                {
                    if(nOffset > 0) // an authentication header was found but not the right one so adjust the error
                    {
                        nRetCode = HTTPCLIENT_ERROR_AUTH_MISMATCH;
                    }
                    else
                    {
                        nRetCode = HTTPCLIENT_ERROR_BAD_HEADER;
                    }

                    break;
                }

                // Make sure that we get an authentication header that maches the caller requested schema
                pPtr = HTTPStrCaseStr(pHTTPSession->HttpAuthHeader.AuthHeader.pParam,
                    pHTTPSession->HttpAuthHeader.AuthHeader.nLength,
                    pHTTPSession->HttpCredentials.AuthSchemaName);
                if(pPtr)
                {
                    AuthHeaders = TRUE;
                }
                else
                {
                    // Simply pass the point where the last "www" was found
                    nOffset = (pHTTPSession->HttpAuthHeader.AuthHeader.pParam - pHTTPSession->HttpHeaders.HeadersIn.pParam) + 3;
                }
            }
            else
            {
                AuthHeaders = TRUE;
            }
        }

        // Is this a proxy authentication header?
        if(pHTTPSession->HttpHeadersInfo.nHTTPStatus == HTTP_STATUS_PROXY_AUTHENTICATION_REQUIRED)
        {
            // Double check for the "Proxy-Authentication" header token
            if (HTTPIntrnHeadersFind(pHTTPSession,"proxy-authenticate",&pHTTPSession->HttpAuthHeader.AuthHeader,TRUE,0) != HTTPCLIENT_SUCCESS)
            {
                nRetCode = HTTPCLIENT_ERROR_BAD_HEADER;
                break;
            }
        }

        // Do we have a redirection response?
        if( (pHTTPSession->HttpHeadersInfo.nHTTPStatus == HTTP_STATUS_OBJECT_MOVED) ||
            (pHTTPSession->HttpHeadersInfo.nHTTPStatus == HTTP_STATUS_OBJECT_MOVED_PERMANENTLY))
        {
            // Check for the "Location" header token
            if (HTTPIntrnHeadersFind(pHTTPSession,"location",&pHTTPSession->HttpHeadersInfo.HttpRedirectURL,TRUE,0) != HTTPCLIENT_SUCCESS)
            {
                // Protocol violation, we got a redirect code without the host name to redirect to
                nRetCode = HTTPCLIENT_ERROR_BAD_HEADER;
                break;
            }
            // Fix the pointers location (address the "Location: " prefix)
            pHTTPSession->HttpHeadersInfo.HttpRedirectURL.pParam += 12;
            pHTTPSession->HttpHeadersInfo.HttpRedirectURL.nLength -= 12;

        }

    }while(0);


    return nRetCode;
}

///////////////////////////////////////////////////////////////////////////////
//
// Function     : HTTPIntrnParseAuthHeader
// Purpose      : Parse the HTTP headers for the required authentication method
// Gets         :
// Returns      : HTTP Status
// Last updated : 01/09/2005
//
///////////////////////////////////////////////////////////////////////////////

static UINT32 HTTPIntrnParseAuthHeader(P_HTTP_SESSION pHTTPSession)
{

    CHAR            *pPtrStart, *pPtrEnd;

    if(!pHTTPSession)
    {
        return HTTPCLIENT_ERROR_INVALID_HANDLE;
    }

    if(pHTTPSession->HttpProxy.ProxyAuthSchema != HTTPCLIENT_AUTHSCHEMA_NONE)
    {
        // for proxy authentication simply assume basic and exit
        return HTTPCLIENT_SUCCESS;
    }
    // Advance the pointer in the string and break on the first space
    pPtrEnd   = pHTTPSession->HttpAuthHeader.AuthHeader.pParam + pHTTPSession->HttpAuthHeader.AuthHeader.nLength;
    pPtrStart = pHTTPSession->HttpAuthHeader.AuthHeader.pParam;
    // Jump to the first space
    while ((pPtrEnd - pPtrStart) > 0 && *pPtrStart != 0x20) pPtrStart++;

    do
    {
        if(HTTPStrCaseStr(pPtrStart,8,"basic"))
        {
            pHTTPSession->HttpAuthHeader.httpclientAuthSchema_e = HTTPCLIENT_AUTHSCHEMA_BASIC;
            break;
        }

        if(HTTPStrCaseStr(pPtrStart,8,"digest"))
        {
            pHTTPSession->HttpAuthHeader.httpclientAuthSchema_e = HTTPCLIENT_AUTHSCHEMA_DIGEST;
            break;
        }
        if(HTTPStrCaseStr(pPtrStart,8,"negotiate")) // Note that this could be NLM negotiation as well (which is not supported)
        {
            pHTTPSession->HttpAuthHeader.httpclientAuthSchema_e = HTTPCLIENT_AUTHSCHEMA_KERBEROS;
            break;
        }
        // To-Do: Add any other supported authentication method
    }
    while(0);

#if defined(_HTTP_DEBUGGING_)
    if(pHTTPSession->pDebug)
    {
        pHTTPSession->pDebug("HTTPIntrnParseAuthHeader",pHTTPSession->HttpAuthHeader.AuthHeader.pParam,
            pHTTPSession->HttpAuthHeader.AuthHeader.nLength,"[Incomming Auth Headers: %d]",pHTTPSession->HttpAuthHeader.httpclientAuthSchema_e);
    }
#endif

    // If we could not detect the authentication schema return an error
    if(pHTTPSession->HttpAuthHeader.httpclientAuthSchema_e == HTTPCLIENT_AUTHSCHEMA_NONE)
    {
        return HTTPCLIENT_ERROR_BAD_AUTH;
    }

    //Make sure we are going to authenticate with the method specified by the caller
    if(pHTTPSession->HttpAuthHeader.httpclientAuthSchema_e != (UINT32)pHTTPSession->HttpCredentials.CredAuthSchema)
    {
        return HTTPCLIENT_ERROR_AUTH_MISMATCH;
    }


    return HTTPCLIENT_SUCCESS;

}


///////////////////////////////////////////////////////////////////////////////
//
// Function     : HTTPIntrnHeadersSend
// Purpose      : Build and send the HTTP request. this includes the HTTP headers
//                and any required authentication data
// Gets         :
// Returns      : HTTP Status
// Last updated : 01/09/2005
//
///////////////////////////////////////////////////////////////////////////////

static UINT32 HTTPIntrnHeadersSend(P_HTTP_SESSION pHTTPSession,
                            httpclientVerb_e HttpVerb)  // [IN] Argument that can bypass the requested verb
                            // Can be used for evaluating a HEAD request
{

    UINT32          nBytes;
    UINT32          nRetCode = HTTPCLIENT_SUCCESS;
    CHAR            RequestCmd[16];
    CHAR            ContentLength[32];
    BOOL            RestoreHeadersFlag = FALSE;
    httpclientVerb_e       HttpCachedVerb;

    if(!pHTTPSession)
    {
        // Bad session pointer error
        return HTTPCLIENT_ERROR_INVALID_HANDLE;
    }
#if defined(_HTTP_DEBUGGING_)
    if(pHTTPSession->pDebug)
    {
        pHTTPSession->pDebug("HTTPIntrnHeadersSend",NULL,
            0,"Using Verb: %d",(INT32)HttpVerb);
    }
#endif
    // Cache the original VERB
    HttpCachedVerb = pHTTPSession->HttpHeaders.HttpVerb;

    do
    {

        // Set the verb (temporarily)
        if(pHTTPSession->HttpHeaders.HttpVerb != HttpVerb)
        {
            if((nRetCode = httpclient_set_verb((HTTPCLIENT_SESSION_HANDLE)pHTTPSession,HttpVerb)) != HTTPCLIENT_SUCCESS)
            {
                break;
            }
        }

        // Remeber this state for later usage
        pHTTPSession->HttpHeaders.HttpLastVerb =  pHTTPSession->HttpHeaders.HttpVerb;

        // If this is a head request we should temporary remove the chunking header and the content length header
        if(pHTTPSession->HttpHeaders.HttpVerb == HTTPCLIENT_HEAD)
        {

            // If send in chunks flag was set
            if((pHTTPSession->HttpFlags & HTTPCLIENT_FLAG_SEND_CHUNKED) == HTTPCLIENT_FLAG_SEND_CHUNKED)
            {
                // Chunking
                if((nRetCode = HTTPIntrnHeadersRemove(pHTTPSession,"Transfer-Encoding")) != HTTPCLIENT_SUCCESS)
                {
                    break;
                }
            }
            // Content-Length
            if(pHTTPSession->HttpHeadersInfo.nHTTPPostContentLength > 0) // Attempt to remove only if it was previusly set
            {
                if((nRetCode = HTTPIntrnHeadersRemove(pHTTPSession,"Content-Length")) != HTTPCLIENT_SUCCESS)
                {
                    break;
                }
            }

            RestoreHeadersFlag = TRUE; // So it would be restored later
        }
        // Request Verb
        nBytes = strlen(pHTTPSession->HttpHeaders.Verb) + 1;
        memset(RequestCmd,0x00,16);
        strcpy(RequestCmd,pHTTPSession->HttpHeaders.Verb);
        strcat(RequestCmd," ");
        if((nRetCode = HTTPIntrnSend(pHTTPSession,RequestCmd,&nBytes)) != HTTPCLIENT_SUCCESS)
        {
            break;
        }
        // Set the counters
        pHTTPSession->HttpCounters.nSentHeaderBytes += nBytes;


        // Request URI
        if((pHTTPSession->HttpFlags & HTTPCLIENT_FLAG_USINGPROXY) != HTTPCLIENT_FLAG_USINGPROXY)
        {
            nBytes = pHTTPSession->HttpUrl.UrlRequest.nLength;
            if((nRetCode = HTTPIntrnSend(pHTTPSession,pHTTPSession->HttpUrl.UrlRequest.pParam,&nBytes)) != HTTPCLIENT_SUCCESS)
            {
                break;
            }
            // Set the counters
            pHTTPSession->HttpCounters.nSentHeaderBytes += nBytes;
        }
        else
        {
            nBytes = strlen(pHTTPSession->HttpUrl.Url);
            if((nRetCode = HTTPIntrnSend(pHTTPSession,pHTTPSession->HttpUrl.Url,&nBytes)) != HTTPCLIENT_SUCCESS)
            {
                break;
            }
            // Set the counters
            pHTTPSession->HttpCounters.nSentHeaderBytes += nBytes;
        }
        // Request HTTP Version
        memset(RequestCmd,0x00,16);
        strcpy(RequestCmd," ");
        strcat(RequestCmd,HTTPCLIENT_DEFAULT_VER);
        strcat(RequestCmd,HTTPCLIENT_CRLF);
        nBytes = strlen(RequestCmd);
        if((nRetCode = HTTPIntrnSend(pHTTPSession,RequestCmd,&nBytes)) != HTTPCLIENT_SUCCESS)
        {
            break;
        }
        // Set the counters
        pHTTPSession->HttpCounters.nSentHeaderBytes += nBytes;

        // Request headers
        nBytes = pHTTPSession->HttpHeaders.HeadersOut.nLength;
        if((nRetCode = HTTPIntrnSend(pHTTPSession,pHTTPSession->HttpHeaders.HeadersOut.pParam,&nBytes)) != HTTPCLIENT_SUCCESS)
        {
            break;
        }
        // Set the counters
        pHTTPSession->HttpCounters.nSentHeaderBytes += nBytes;

        // Optionally add authentication headers and send them (for host or proxy authentication)
        if(pHTTPSession->HttpHeadersInfo.nHTTPStatus == HTTP_STATUS_UNAUTHORIZED || pHTTPSession->HttpHeadersInfo.nHTTPStatus == HTTP_STATUS_PROXY_AUTHENTICATION_REQUIRED  )
        {

            if((nRetCode = HTTPIntrnAuthHandler(pHTTPSession)) != HTTPCLIENT_SUCCESS)
            {
                break;
            }
        }

        // Request terminating CrLf
        nBytes = strlen(HTTPCLIENT_CRLF);
        if((nRetCode = HTTPIntrnSend(pHTTPSession,HTTPCLIENT_CRLF,&nBytes)) != HTTPCLIENT_SUCCESS)
        {
            break;
        }
        // Set the counters
        pHTTPSession->HttpCounters.nSentHeaderBytes += nBytes;

        // Restore the verb
        if(pHTTPSession->HttpHeaders.HttpVerb != HttpCachedVerb)
        {
            if((nRetCode = httpclient_set_verb((HTTPCLIENT_SESSION_HANDLE)pHTTPSession,HttpCachedVerb)) != HTTPCLIENT_SUCCESS)
            {
                break;
            }
        }

        if(RestoreHeadersFlag == TRUE)
        {
            // Restore chunking header (since it was temporarily removed for the head request
            // Add the  Transfer-Encoding:  header
            if((pHTTPSession->HttpFlags & HTTPCLIENT_FLAG_SEND_CHUNKED) == HTTPCLIENT_FLAG_SEND_CHUNKED)
            {
                if((nRetCode = HTTPIntrnHeadersAdd(pHTTPSession,"Transfer-Encoding",17,"chunked",7))!= HTTPCLIENT_SUCCESS)
                {
                    break;
                }
            }
            // Restore the content length
            if(pHTTPSession->HttpHeadersInfo.nHTTPPostContentLength > 0) // Attempt to remove only if it was previusly set
            {
                IToA(ContentLength,pHTTPSession->HttpHeadersInfo.nHTTPPostContentLength); // Convert the buffer length to a string value
                if((nRetCode = HTTPIntrnHeadersAdd(pHTTPSession,"Content-Length",14,ContentLength,strlen(ContentLength)))!= HTTPCLIENT_SUCCESS)
                {
                    return nRetCode;
                }
            }
        }
        // Set the session stage
        pHTTPSession->HttpState = pHTTPSession->HttpState | HTTPCLIENT_STATE_REQUEST_SENT;

    } while(0);


    return nRetCode;     // end of HTTPIntrnSendHeaders()
}

///////////////////////////////////////////////////////////////////////////////
//
// Function     : HTTPIntrnAuthHandler
// Purpose      : Differentiate between the authenticate method that we have to implement and perform
//                the required operation.
// Gets         :
// Returns      : HTTP Status
// Last updated : 01/09/2005
//
///////////////////////////////////////////////////////////////////////////////

static UINT32 HTTPIntrnAuthHandler (P_HTTP_SESSION pHTTPSession)
{

    UINT32 nRetCode = HTTPCLIENT_SUCCESS;

    if(!pHTTPSession)
    {
        // Bad session pointer error
        return HTTPCLIENT_ERROR_INVALID_HANDLE;
    }

    if(pHTTPSession->HttpProxy.ProxyAuthSchema != HTTPCLIENT_AUTHSCHEMA_NONE)
    {
        // For proxy authentication simply assume basic and exit
        // Basic authentication
        nRetCode = HTTPIntrnAuthSendBasic(pHTTPSession);
        return nRetCode;
    }


#if defined(_HTTP_DEBUGGING_)
    if(pHTTPSession->pDebug)
    {
        pHTTPSession->pDebug("HTTPIntrnAuthHandler",NULL,
            0,"");
    }
#endif

    // Use the correct authentication method as requested by the server
    switch(pHTTPSession->HttpAuthHeader.httpclientAuthSchema_e)
    {

    case HTTPCLIENT_AUTHSCHEMA_BASIC:
        {
            // Basic authentication
            nRetCode = HTTPIntrnAuthSendBasic(pHTTPSession);
            break;

        }
    case HTTPCLIENT_AUTHSCHEMA_DIGEST:
        {
            // Digest authentication
            nRetCode = HTTPIntrnAuthSendDigest(pHTTPSession);
            break;

        }
    case HTTPCLIENT_AUTHSCHEMA_KERBEROS:
        {
            // ToDo: impliament the Kerberos nego authentication here
            nRetCode = HTTPCLIENT_ERROR_NOT_IMPLEMENTED;
            break;

        }
    default:
        {
            // Not supported method
            return HTTPCLIENT_ERROR_BAD_AUTH; // Not implemented error
        }

    };

    // This session requested an authentication so..
    pHTTPSession->HttpCredentials.Authentication = TRUE;
    return nRetCode;
}

///////////////////////////////////////////////////////////////////////////////
//
// Function     : HTTPIntrnAuthSendBasic
// Purpose      : Handle basic authentication for direst host connection and proxy authentication
// Gets         :
// Returns      :
// Last updated : 01/09/2005
//
///////////////////////////////////////////////////////////////////////////////


static UINT32 HTTPIntrnAuthSendBasic (P_HTTP_SESSION pHTTPSession)
{

    UINT32      nSegmentLength;
    UINT32      nRetCode;
    CHAR        Cred[HTTPCLIENT_MAX_64_ENCODED_CRED /2];   // Credentials (Clear)
    CHAR        Cred64[HTTPCLIENT_MAX_64_ENCODED_CRED];    // Credentials (64 bit encoded)
    UINT32      nSrcLength, nDestLength;
    CHAR*       pPtr;
    CHAR*       INITIAL_HDR         = "Authorization: Basic ";
    CHAR*       INITIAL_PROXY_HDR   = "Proxy-Authorization: Basic ";


    do
    {
        if(!pHTTPSession)
        {
            nRetCode = HTTPCLIENT_ERROR_INVALID_HANDLE;
            break;
        }

#if defined(_HTTP_DEBUGGING_)
        if(pHTTPSession->pDebug)
        {
            pHTTPSession->pDebug("HTTPIntrnAuthSendBasic",NULL,
                0,"");
        }
#endif

        memset(Cred,0x00,HTTPCLIENT_MAX_64_ENCODED_CRED /2);
        memset(Cred64,0x00,HTTPCLIENT_MAX_64_ENCODED_CRED);


        switch (pHTTPSession->HttpHeadersInfo.nHTTPStatus)
        {
        case( HTTP_STATUS_UNAUTHORIZED): // For host authentication
            {

                // Copy the clear text credentials to a format of user:password
                strcpy(Cred,pHTTPSession->HttpCredentials.CredUser);
                strcat(Cred,":");
                strcat(Cred,pHTTPSession->HttpCredentials.CredPassword);
                nSrcLength  = strlen(Cred);
                nDestLength = HTTPCLIENT_MAX_64_ENCODED_CRED;
                nSegmentLength = strlen(INITIAL_HDR);
                // Build and send the data first the hard-coded static portion
                if((nRetCode = HTTPIntrnSend(pHTTPSession,INITIAL_HDR, &nSegmentLength)) != HTTPCLIENT_SUCCESS)
                {
                    break;
                }
                // Set the counters
                pHTTPSession->HttpCounters.nSentHeaderBytes += nSegmentLength;

                // Convert to base 64
                HTTPBase64Encoder((unsigned char *)Cred64,(const unsigned char *)Cred,nSrcLength);
                nDestLength = strlen(Cred64);

            };
            break;
        case (HTTP_STATUS_PROXY_AUTHENTICATION_REQUIRED): // For Proxy authentication
            {
                // Copy the clear text credentials to a format of user:password
                strcpy(Cred,pHTTPSession->HttpProxy.ProxtUser);
                strcat(Cred,":");
                strcat(Cred,pHTTPSession->HttpProxy.ProxyPassword);
                nSrcLength  = strlen(Cred);
                nDestLength = HTTPCLIENT_MAX_64_ENCODED_CRED;

                // Convert to base 64
                HTTPBase64Encoder((unsigned char *)Cred64,(unsigned char *)Cred,nSrcLength);
                nDestLength = strlen(Cred64);
                nSegmentLength = strlen(INITIAL_PROXY_HDR);
                // Build and send the data first the hard-coded static portion
                if((nRetCode = HTTPIntrnSend(pHTTPSession,INITIAL_PROXY_HDR, &nSegmentLength)) != HTTPCLIENT_SUCCESS)
                {
                    break;
                }
                // Set the counters
                pHTTPSession->HttpCounters.nSentHeaderBytes += nSegmentLength;

            };
            break;
        default:
            {
                return HTTPCLIENT_ERROR_BAD_AUTH; // Wrong status for this function
            };
        };

        // Send the base 64 encoded data
        pPtr = Cred64;
        if((nRetCode = HTTPIntrnSend(pHTTPSession,pPtr, &nDestLength)) != HTTPCLIENT_SUCCESS)
        {
            break;
        }
        // Set the counters
        pHTTPSession->HttpCounters.nSentHeaderBytes += nDestLength;

        // Terminating CRLF
        nSegmentLength = strlen(HTTPCLIENT_CRLF);
        if((nRetCode = HTTPIntrnSend(pHTTPSession,HTTPCLIENT_CRLF, &nSegmentLength)) != HTTPCLIENT_SUCCESS)
        {
            break;
        }
        // Set the counters
        pHTTPSession->HttpCounters.nSentHeaderBytes += nSegmentLength;


    } while (0);

    return nRetCode;

}

///////////////////////////////////////////////////////////////////////////////
//
// Function     : HTTPIntrnAuthSendDigest
// Purpose      : Handle digest authentication for direct host connection and proxy authentication
// Gets         :
// Returns      : HTTP Status
// Last updated : 01/09/2005
//
///////////////////////////////////////////////////////////////////////////////

static UINT32 HTTPIntrnAuthSendDigest (P_HTTP_SESSION pHTTPSession)
{
    CHAR        Cnonce[33];
    UINT32      nSegmentLength;
    UINT32      nRetCode;
    UINT32      nAlgType = 0; // a flag for the algorithem type (default to MD5)
    httpclientParam_t  HttpParamOpq,HttpParamRealm,HttpParamNonce,HttpParamQop,HttpParamAlg;     // Pointers and lengths of the dynamic sections
    // of the Digest response.


    // Fragments of the Digest client response (The hard coded text portion of the response)
    CHAR*       INITIAL_HDR         = "Authorization: Digest username=\"";
    CHAR*       INITIAL_PROXY_HDR   = "Proxy-Authorization: Digest username=\"";
    CHAR*       REALEM_HDR          = "\", realm=\"";
    CHAR*       QOP_HDR             = "\", qop=\"";
    CHAR*       ALGO_HDR            = "\", algorithm=\"";
    CHAR*       URI_HDR             = "\", uri=\"";
    CHAR*       NONCE_HDR           = "\", nonce=\"";
    CHAR*       NC_HDR              = "\", nc=00000001, cnonce=\""; // To-Do: This should be tested!!
    CHAR*       RSP_HDR             = "\", response=\"";
    CHAR*       OPQ_HDR             = "\", opaque=\"";
    // Digest Calculation related
    HASHHEX     HA1;
    HASHHEX     HA2 = "";
    HASHHEX     Response;

    if(!pHTTPSession)
    {
        return HTTPCLIENT_ERROR_INVALID_HANDLE;
    }

#if defined(_HTTP_DEBUGGING_)
    if(pHTTPSession->pDebug)
    {
        pHTTPSession->pDebug("HTTPIntrnAuthSendDigest",NULL,
            0,"");
    }
#endif

    // Generate random Cnonce number
    HTTPDigestGenerateCNonce(Cnonce);

    switch (pHTTPSession->HttpHeadersInfo.nHTTPStatus)
    {
    case( HTTP_STATUS_UNAUTHORIZED): // For host authentication
        {
            // "Authorization: Digest username="
            nSegmentLength = strlen(INITIAL_HDR);
            if((nRetCode = HTTPIntrnSend(pHTTPSession,INITIAL_HDR, &nSegmentLength)) != HTTPCLIENT_SUCCESS)
            {
                return nRetCode;
            }
            // Set the counters
            pHTTPSession->HttpCounters.nSentHeaderBytes += nSegmentLength;
        };
        break;
    case (HTTP_STATUS_PROXY_AUTHENTICATION_REQUIRED): // For Proxy authentication
        {
            // "Proxy-Authorization: Digest username="
            nSegmentLength = strlen(INITIAL_PROXY_HDR);
            if((nRetCode = HTTPIntrnSend(pHTTPSession,INITIAL_PROXY_HDR, &nSegmentLength)) != HTTPCLIENT_SUCCESS)
            {
                return nRetCode;
            }
            // Set the counters
            pHTTPSession->HttpCounters.nSentHeaderBytes += nSegmentLength;
        };
        break;
    default:
        {
            return HTTPCLIENT_ERROR_BAD_AUTH; // Wrong status for this function
        };
    };

    do
    {

        // "Authorization: Digest username="username
        nSegmentLength = strlen(pHTTPSession->HttpCredentials.CredUser);
        if((nRetCode = HTTPIntrnSend(pHTTPSession,pHTTPSession->HttpCredentials.CredUser, &nSegmentLength)) != HTTPCLIENT_SUCCESS)
        {
            break;
        }
        // Set the counters
        pHTTPSession->HttpCounters.nSentHeaderBytes += nSegmentLength;

        // "Authorization: Digest username="username", realm="
        nSegmentLength = strlen(REALEM_HDR);
        if((nRetCode = HTTPIntrnSend(pHTTPSession,REALEM_HDR, &nSegmentLength)) != HTTPCLIENT_SUCCESS)
        {
            break;
        }
        // Set the counters
        pHTTPSession->HttpCounters.nSentHeaderBytes += nSegmentLength;

        // "Authorization: Digest username="username", realm="realm
        if((nRetCode = HTTPStrGetDigestToken(pHTTPSession->HttpAuthHeader.AuthHeader,"realm", &HttpParamRealm)) != HTTPCLIENT_SUCCESS)
        {
            break;
        }
        if((nRetCode = HTTPIntrnSend(pHTTPSession,HttpParamRealm.pParam, &HttpParamRealm.nLength)) != HTTPCLIENT_SUCCESS)
        {
            break;
        }
        // Set the counters
        pHTTPSession->HttpCounters.nSentHeaderBytes += HttpParamRealm.nLength;

        // "Authorization: Digest username="username", realm="myRealm", qop="auth",
        nSegmentLength = strlen(QOP_HDR);
        if((nRetCode = HTTPIntrnSend(pHTTPSession,QOP_HDR, &nSegmentLength)) != HTTPCLIENT_SUCCESS)
        {
            break;
        }
        // Set the counters
        pHTTPSession->HttpCounters.nSentHeaderBytes += nSegmentLength;

        if((nRetCode = HTTPStrGetDigestToken(pHTTPSession->HttpAuthHeader.AuthHeader,"qop", &HttpParamQop)) != HTTPCLIENT_SUCCESS)
        {
            break;
        }
        if((nRetCode = HTTPIntrnSend(pHTTPSession,HttpParamQop.pParam, &HttpParamQop.nLength)) != HTTPCLIENT_SUCCESS)
        {
            break;
        }
        // Set the counters
        pHTTPSession->HttpCounters.nSentHeaderBytes += HttpParamQop.nLength;

        // "Authorization: Digest username="username", realm="myRealm", qop="auth",
        // algorithm="MD5",

        nSegmentLength = strlen(ALGO_HDR);
        if((nRetCode = HTTPIntrnSend(pHTTPSession,ALGO_HDR, &nSegmentLength)) != HTTPCLIENT_SUCCESS)
        {
            break;
        }
        // Set the counters
        pHTTPSession->HttpCounters.nSentHeaderBytes += nSegmentLength;

        if((nRetCode = HTTPStrGetDigestToken(pHTTPSession->HttpAuthHeader.AuthHeader,"algorithm", &HttpParamAlg)) != HTTPCLIENT_SUCCESS)
        {

            // The server did not state its required algorithm so use the default
            HttpParamAlg.pParam  =   HTTPCLIENT_DEFAULT_DIGEST_AUTH;
            HttpParamAlg.nLength =   strlen(HTTPCLIENT_DEFAULT_DIGEST_AUTH);
        }
        // Get the algorithem type
        if(HTTPStrInsensitiveCompare(HttpParamAlg.pParam ,"md5",3 ) == TRUE)
        {
            if(HTTPStrInsensitiveCompare(HttpParamAlg.pParam ,"md5-sess", HttpParamAlg.nLength) == TRUE)
            {
                nAlgType = 1;
            }

        }
        else
        {
            // Error algorithem not supported
            nRetCode = HTTPCLIENT_ERROR_NO_DIGEST_ALG;
            break;
        }

        // Send the algorithem
        if((nRetCode = HTTPIntrnSend(pHTTPSession,HttpParamAlg.pParam, &HttpParamAlg.nLength)) != HTTPCLIENT_SUCCESS)
        {
            break;
        }
        // Set the counters
        pHTTPSession->HttpCounters.nSentHeaderBytes += HttpParamAlg.nLength;


        // "Authorization: Digest username="username", realm="myRealm", qop="auth",
        // algorithm="MD5", uri="
        nSegmentLength = strlen(URI_HDR);
        if((nRetCode = HTTPIntrnSend(pHTTPSession,URI_HDR, &nSegmentLength)) != HTTPCLIENT_SUCCESS)
        {
            break;
        }

        // "Authorization: Digest username="username", realm="myRealm", qop="auth",
        // algorithm="MD5", uri="/....Service
        nSegmentLength = strlen(pHTTPSession->HttpUrl.UrlRequest.pParam);
        if((nRetCode = HTTPIntrnSend(pHTTPSession,pHTTPSession->HttpUrl.UrlRequest.pParam, &nSegmentLength)) != HTTPCLIENT_SUCCESS)
        {
            break;
        }
        // Set the counters
        pHTTPSession->HttpCounters.nSentHeaderBytes += nSegmentLength;


        // "Authorization: Digest username="username", realm="myRealm", qop="auth",
        // algorithm="MD5", uri="/....Service", nonce="
        nSegmentLength = strlen(NONCE_HDR);
        if((nRetCode = HTTPIntrnSend(pHTTPSession,NONCE_HDR, &nSegmentLength)) != HTTPCLIENT_SUCCESS)
        {
            break;
        }
        // Set the counters
        pHTTPSession->HttpCounters.nSentHeaderBytes += nSegmentLength;


        // "Authorization: Digest username="username", realm="myRealm", qop="auth",
        // algorithm="MD5", uri="/....Service", nonce="7a5c...
        if((nRetCode = HTTPStrGetDigestToken(pHTTPSession->HttpAuthHeader.AuthHeader,"nonce", &HttpParamNonce)) != HTTPCLIENT_SUCCESS)
        {
            break;
        }
        if((nRetCode = HTTPIntrnSend(pHTTPSession,HttpParamNonce.pParam, &HttpParamNonce.nLength)) != HTTPCLIENT_SUCCESS)
        {
            break;
        }
        // Set the counters
        pHTTPSession->HttpCounters.nSentHeaderBytes += HttpParamNonce.nLength;

        // "Authorization: Digest username="username", realm="myRealm", qop="auth",
        // algorithm="MD5", uri="/....Service", nonce="7a5c...", nc=00000001, cnonce="
        nSegmentLength = strlen(NC_HDR);
        if((nRetCode = HTTPIntrnSend(pHTTPSession,NC_HDR, &nSegmentLength)) != HTTPCLIENT_SUCCESS)
        {
            break;
        }
        // Set the counters
        pHTTPSession->HttpCounters.nSentHeaderBytes += nSegmentLength;

        // "Authorization: Digest username="username", realm="myRealm", qop="auth",
        // algorithm="MD5", uri="/....Service", nonce="7a5c...", nc=00000001, cnonce="ab341...
        nSegmentLength = strlen(Cnonce);
        if((nRetCode = HTTPIntrnSend(pHTTPSession,Cnonce, &nSegmentLength)) != HTTPCLIENT_SUCCESS)
        {
            break;
        }
        // Set the counters
        pHTTPSession->HttpCounters.nSentHeaderBytes += nSegmentLength;

        // Send the opaque data if we got it from the server
        if((nRetCode = HTTPStrGetDigestToken(pHTTPSession->HttpAuthHeader.AuthHeader,"opaque", &HttpParamOpq)) == HTTPCLIENT_SUCCESS)
        {

            nSegmentLength = strlen(OPQ_HDR);
            if((nRetCode = HTTPIntrnSend(pHTTPSession,OPQ_HDR, &nSegmentLength)) != HTTPCLIENT_SUCCESS)
            {
                break;
            }
            // Set the counters
            pHTTPSession->HttpCounters.nSentHeaderBytes += nSegmentLength;
            if((nRetCode = HTTPIntrnSend(pHTTPSession,HttpParamOpq.pParam, &HttpParamOpq.nLength)) != HTTPCLIENT_SUCCESS)
            {
                break;
            }
            // Set the counters
            pHTTPSession->HttpCounters.nSentHeaderBytes += HttpParamOpq.nLength;

        }

        // "Authorization: Digest username="username", realm="myRealm", qop="auth",
        // algorithm="MD5", uri="/....Service", nonce="7a5c...", nc=00000001, cnonce="ab341...", response="
        nSegmentLength = strlen(RSP_HDR);
        if((nRetCode = HTTPIntrnSend(pHTTPSession,RSP_HDR, &nSegmentLength)) != HTTPCLIENT_SUCCESS)
        {
            break;
        }
        // Set the counters
        pHTTPSession->HttpCounters.nSentHeaderBytes += nSegmentLength;

        // Calculate response
        HTTPDigestCalcHA1(nAlgType, pHTTPSession->HttpCredentials.CredUser,
            HttpParamRealm.pParam,HttpParamRealm.nLength ,
            pHTTPSession->HttpCredentials.CredPassword ,
            HttpParamNonce.pParam, HttpParamNonce.nLength,
            Cnonce, HA1);

        HTTPDigestCalcResponse(HA1,
            HttpParamNonce.pParam, HttpParamNonce.nLength,
            "00000001", Cnonce,
            HttpParamQop.pParam,HttpParamQop.nLength, pHTTPSession->HttpHeaders.Verb,
            pHTTPSession->HttpUrl.UrlRequest.pParam,pHTTPSession->HttpUrl.UrlRequest.nLength,
            HA2, Response);

        // "Authorization: Digest username="username", realm="myRealm", qop="auth",
        // algorithm="MD5", uri="/....Service", nonce="7a5c...", nc=00000001, cnonce="ab341...", response="8bbf2...
        nSegmentLength = strlen(Response);
        if((nRetCode = HTTPIntrnSend(pHTTPSession,Response, &nSegmentLength)) != HTTPCLIENT_SUCCESS)
        {
            break;
        }
        // Set the counters
        pHTTPSession->HttpCounters.nSentHeaderBytes += nSegmentLength;


        // Terminate 0x24 (")
        nSegmentLength = 1;
        if((nRetCode = HTTPIntrnSend(pHTTPSession,"\"", &nSegmentLength)) != HTTPCLIENT_SUCCESS)
        {
            break;
        }
        // Set the counters
        pHTTPSession->HttpCounters.nSentHeaderBytes += nSegmentLength;

        // Terminating CRLF
        nSegmentLength = strlen(HTTPCLIENT_CRLF);
        if((nRetCode = HTTPIntrnSend(pHTTPSession,HTTPCLIENT_CRLF, &nSegmentLength)) != HTTPCLIENT_SUCCESS)
        {
            break;
        }
        // Set the counters
        pHTTPSession->HttpCounters.nSentHeaderBytes += nSegmentLength;

    } while(0);

    return nRetCode; // End of digest respobse sending
}

///////////////////////////////////////////////////////////////////////////////
//
// Function     : HTTPIntrnSessionReset
// Purpose      : Reset the session data for the next operation
// Returns      : HTTP Status
// Last updated : 01/09/2005
//
///////////////////////////////////////////////////////////////////////////////

static UINT32 HTTPIntrnSessionReset (P_HTTP_SESSION pHTTPSession, BOOL EntireSession)
{
    UINT32 nActionTimeout; // For restoring a parameter after this reset
    UINT32 nAllocationSize;

    // Validate the pointer
    if(!pHTTPSession)
    {
        return HTTPCLIENT_ERROR_INVALID_HANDLE;
    }

#if defined(_HTTP_DEBUGGING_)
    if(pHTTPSession->pDebug)
    {
        pHTTPSession->pDebug("HTTPIntrnSessionReset",NULL,
            0,"");
    }
#endif


    memset(pHTTPSession->HttpHeaders.HeadersIn.pParam,0x00,pHTTPSession->HttpHeaders.HeadersIn.nLength);
    pHTTPSession->HttpHeaders.HeadersIn.nLength = 0;


    // Reset the HTTP counters
    nActionTimeout = pHTTPSession->HttpCounters.nActionTimeout;
    memset(&pHTTPSession->HttpCounters,0x00,sizeof(httpclientCounters_t));
    pHTTPSession->HttpCounters.nActionStartTime = HTTPIntrnSessionGetUpTime();
    // Restore the parameter
    pHTTPSession->HttpCounters.nActionTimeout = nActionTimeout;
    // Reset the authentication flag
    pHTTPSession->HttpCredentials.Authentication = FALSE;


    if(EntireSession == TRUE) // Partial reset, clear only the incoming headers
    {
        memset(&pHTTPSession->HttpUrl,0,sizeof(httpclientUrl_t));
        nAllocationSize =  pHTTPSession->HttpHeaders.HeadersBuffer.nLength;
        // Reset the headers allocated memory
        memset(pHTTPSession->HttpHeaders.HeadersBuffer.pParam ,0x00,nAllocationSize);

        // Set default values in the session structure
        httpclient_set_verb((UINTP)pHTTPSession,(httpclientVerb_e)HTTPCLIENT_DEFAULT_VERB);    // Default HTTP verb
        pHTTPSession->HttpUrl.nPort             = HTTPCLIENT_DEFAULT_PORT;             // Default TCP port
        // Set the outgoing headers pointers
        memset(&pHTTPSession->HttpHeaders.HeadersIn,0,sizeof(httpclientParam_t));
        memset(&pHTTPSession->HttpHeaders.HeadersOut,0,sizeof(httpclientParam_t));

        pHTTPSession->HttpHeaders.HeadersOut.pParam = pHTTPSession->HttpHeaders.HeadersBuffer.pParam;
        // Set our state
        pHTTPSession->HttpState = pHTTPSession->HttpState | HTTPCLIENT_STATE_INIT;

        memset(&pHTTPSession->HttpHeadersInfo,0,sizeof(httpclientHeadersInfo_t));
        if(pHTTPSession->HttpConnection.HttpSocket != HTTP_INVALID_SOCKET)
        {
            pHTTPSession->HttpHeadersInfo.Connection = TRUE;
        }
        memset(&pHTTPSession->HttpAuthHeader,0,sizeof(httpclientAuthHeader_t));
        memset(&pHTTPSession->HttpProxy,0,sizeof(httpclientProxy_t));

    }

    return HTTPCLIENT_SUCCESS;

}


///////////////////////////////////////////////////////////////////////////////
//
// Function     : HTTPIntrnHeadersReceive
// Purpose      : Receives the response header on the connection and parses it.
//                Performs any required authentication.
// Returns      : HTTP Status
// Last updated : 01/09/2005
//
///////////////////////////////////////////////////////////////////////////////

static UINT32 HTTPIntrnHeadersReceive (P_HTTP_SESSION pHTTPSession,
                                UINT32 nTimeout)        // [IN] Timeout for the operation

{

    UINT32          nRetCode;               // Function call return code
    UINT32          nCount  = 0;
    if(!pHTTPSession)
    {
        // Bad session pointer error
        return HTTPCLIENT_ERROR_INVALID_HANDLE;
    }

    do
    {


        // Set the operation time out if was set by the caller
        //if(nTimeout > 0)
        //{
            // 0 makes us use the default defined value
            pHTTPSession->HttpCounters.nActionTimeout = HTTP_TIMEOUT(nTimeout);
        //}

        // Reset the incoming headers
        if((nRetCode = HTTPIntrnSessionReset(pHTTPSession,FALSE)) != HTTPCLIENT_SUCCESS)
        {
            break;
        }

        // Get the server response
        if((nRetCode = HTTPIntrnGetRemoteHeaders(pHTTPSession)) != HTTPCLIENT_SUCCESS)
        {
            break;
        }

        // Set the session state
        pHTTPSession->HttpState = pHTTPSession->HttpState | HTTPCLIENT_STATE_HEADERS_RECIVED;

        // Parse the response headers
        if((nRetCode = HTTPIntrnHeadersParse(pHTTPSession)) != HTTPCLIENT_SUCCESS)
        {
            break;
        }

        // Set the session state
        pHTTPSession->HttpState = pHTTPSession->HttpState | HTTPCLIENT_STATE_HEADERS_PARSED;

        // Set the session stage upon seccess
        if(pHTTPSession->HttpHeadersInfo.nHTTPStatus == HTTP_STATUS_OK)
        {
            pHTTPSession->HttpState = pHTTPSession->HttpState | HTTPCLIENT_STATE_HEADERS_OK;
        }
        // Handle 100 continue message
        if(pHTTPSession->HttpHeadersInfo.nHTTPStatus != HTTP_STATUS_CONTINUE)
        {
            nCount++;
        }

#if defined(_HTTP_DEBUGGING_)
        if(pHTTPSession->pDebug)
        {
            if(pHTTPSession->HttpHeadersInfo.nHTTPStatus == HTTP_STATUS_CONTINUE)
            {
                pHTTPSession->pDebug("HTTPIntrnHeadersReceive",NULL,0,"100 Continue Header");
            }
        }
#endif

    }while(nCount < 1);

    return nRetCode;
}

///////////////////////////////////////////////////////////////////////////////
//
// Function     : HTTPIntrnGetTicks
// Purpose      : Like GetTickCount() (implemented with time.h)
// Gets         : void
// Returns      : System ticks
// Last updated : 01/09/200515/05/2005
// Author Name	: Eitan Michaelson
// Notes	    : Assuming 1000 ticks per sec, should be implemented by the OS
//
///////////////////////////////////////////////////////////////////////////////

static UINT32 HTTPIntrnSessionGetUpTime(VOID)
{

    return GetUpTime();

}

///////////////////////////////////////////////////////////////////////////////
//
// Function     : HTTPIntrnSessionEvalTimeout
// Purpose      : Check if we have to break the operation and return a time out error
// Gets         : a pointer to the session structure
// Returns      : BOOL, True if we have to break
// Last updated : 01/09/2005
//
///////////////////////////////////////////////////////////////////////////////

static BOOL HTTPIntrnSessionEvalTimeout(P_HTTP_SESSION pHTTPSession)
{
#ifdef	NO_TIMEOUTS
	return FALSE;
#else
    UINT32 nElapsedTime;    // Integer for calculating the elapsed time

    // Validate the session pointer
    if(!pHTTPSession)
    {
        return HTTPCLIENT_ERROR_INVALID_HANDLE;
    }

    // Calculate the elapsed time since the last call
    nElapsedTime = HTTPIntrnSessionGetUpTime() - pHTTPSession->HttpCounters.nActionStartTime;
    // If the elapsed time is greater then the time out value we should return true
    if(nElapsedTime >= pHTTPSession->HttpCounters.nActionTimeout)
    {
        return TRUE;
    }

    return FALSE;
#endif
}
